I have a simple spring boot app and I'm trying to get it up and running. The config consists of an app context ( applicationContext.xml) XML with a bunch of beans in it. I have a Spring application class:
#SpringBootApplication
#Configuration
#ImportResource("classpath:applicationContext.xml")
public class WebCheckApplication {
private static final Logger logger = Logger.getLogger(WebCheckApplication.class);
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(WebCheckApplication.class, args);
if (logger.isDebugEnabled()) {
logger.debug("Let's inspect the beans provided by Spring Boot:");
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
logger.debug(beanName);
}
}
}
}
And I have a #WebListener class that grabs a few beans from the WebContext from within the ServletContext:
#WebListener
public class SystemPropertiesContextInitializer extends SysPropsAlertsFetcher implements ServletContextListener {
private static final Logger logger = Logger.getLogger(SystemPropertiesContextInitializer.class);
#Override
public void contextDestroyed(ServletContextEvent sce) {
//remove the SystemProperties and alert types map object from context
sce.getServletContext().removeAttribute(BaseAuthenticatedController.SYSPROPS_KEY);
sce.getServletContext().removeAttribute(BaseAuthenticatedController.ALERT_TYPES_MAP_KEY);
}
#Override
public void contextInitialized(ServletContextEvent sce) {
SysPropsDataAccess = (SystemPropertiesDataAccess) WebApplicationContextUtils.getRequiredWebApplicationContext(sce.getServletContext()).getBean("SystemPropertiesDataAccess");
AlertsDataAccess = (AlertDataAccess) WebApplicationContextUtils.getRequiredWebApplicationContext(sce.getServletContext()).getBean("AlertsDataAccess");
fetchObjects(sce.getServletContext());
}
}
When I attempt to start the app, I get the following error:
SEVERE: Exception sending context initialized event to listener instance of class web.SystemPropertiesContextInitializer
java.lang.IllegalStateException: No WebApplicationContext found: no ContextLoaderListener registered?
at org.springframework.web.context.support.WebApplicationContextUtils.getRequiredWebApplicationContext(WebApplicationContextUtils.java:83)
at .web.SystemPropertiesContextInitializer.contextInitialized(SystemPropertiesContextInitializer.java:31)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4994)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5492)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
and it occurs at this line:
SysPropsDataAccess = (SystemPropertiesDataAccess) WebApplicationContextUtils.getRequiredWebApplicationContext(sce.getServletContext()).getBean("SystemPropertiesDataAccess");
It looks like Spring isn't creating a WebApplicationContext.
Greater than or equal 1.3.0.RC1 use #ServletComponentScan
#ServletComponentScan // <-- This scans for EJB #WebFilter, #WebListener and #WebServlet
#SpringBootApplication
#ImportResource("classpath:applicationContext.xml")
public class WebCheckApplication {
Less than or equal 1.2.x use #Component to scan for listener
#Component // <-- This allows the component to be found by #ComponentScan inside of #SpringBootApplication
#WebListener
public class MojoSystemPropertiesContextInitializer extends MojoSysPropsAlertsFetcher implements ServletContextListener {
War Deploy extend SpringBootServletInitializer
public class WebCheckApplication extends SpringBootServletInitializer {
In 1.3.0.RC1 #ServletComponentScan was added so simply annotating your main application config should allow these to be picked up. Otherwise adding #Component to your ServletContextListener should work
This link is a discussion on how they currently handle #WebFilter how they decided to handle #WebFilter and they also discuss SpringBootServletInitializer and how this would pick process each item twice if both were to be used. Also links to the commits that implement the new feature.
https://github.com/spring-projects/spring-boot/issues/2290
If you intend to deploy your application as a war file you may also have your main configuration extend SpringBootServletInitializer
http://docs.spring.io/spring-boot/docs/current/reference/html/howto-traditional-deployment.html
All that was needed was to make the Application class extend SpringBootServletInitializer
Related
My use case is running custom code before a controller method by annotating methods.
HandlerInterceptor seems the way to go but it seems impossible to inject dependencies into it because it needs to be registered before the context is being created.
All examples I've found so far use empty constructors (see spring boot adding http request interceptors) or autowire properties in the configuration which fails because I declare dependent beans in the same configuration (Requested bean is currently in creation: Is there an unresolvable circular reference?).
Is there a better way that does not involve AOP?
Assume that your interceptor has constructor dependencies like that:
public class CustomInterceptor extends HandlerInterceptor {
private final DependentBean bean;
public CustomInterceptor(DependentBean bean) {
this.bean = bean;
}
}
Then you can register your handler like that:
#Configuration
public WebConfig extends WebMvcConfigurerAdapater {
#Bean
public DependentBean dependentBean() {
return new DependentBean();
}
#Bean
public CustomInterceptor customInterceptor() {
return new CustomInterceptor(dependentBean());
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(customInterceptor());
}
}
#Configuration will ensure each Bean method call return the same bean instance
Building on the answer above from Mạnh, if using component scan for dependency injection of the dependency, then that can be Autowired in the WebConfig
#Configuration
public WebConfig extends WebMvcConfigurerAdapater {
#Autowired
DependentBean dependentBean;
#Bean
public CustomInterceptor customInterceptor() {
return new CustomInterceptor(dependentBean);
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(customInterceptor());
}
}
Also building on previous answers, and if you use Lombok, you can further simplify.
Have your interceptor implementation been a #Component
Add a private final DependentBean field to it.
Also add a #RequiredArgsConstructor annotation to it, to have Lombok generating a constructor with a single DependentBean parameter.
In your WebConfig, use the same technic to have a private final CustomInterceptor field been injected by Spring IOC.
This way the CustomInterceptor instance will be available & initialized the right way when addInterceptors will be called
Here are the corresponding code samples :
The CustomInterceptor :
#Component
#RequiredArgsConstructor
public class CustomInterceptor implements HandlerInterceptor {
private final DependentBean dependentBean;
#Override
public boolean preHandle( final HttpServletRequest request,
final HttpServletResponse response,
final Object handler ) throws Exception {
// your Interceptor Implementation goes here ...
}
}
The WebConfig :
#Configuration
#RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {
private final CustomInterceptor customInterceptor;
#Override
public void addInterceptors( final InterceptorRegistry registry ) {
registry.addInterceptor( customInterceptor );
}
}
I have a class derived from AbstractPreAuthenticatedProcessingFilter in my Spring Security Filter Chain. The purpose of this filter is to massage role data left in a special Principal object by a corporate authentication service into a Collection so SpringSecurity can use them.
However, I cannot get past this exception:
Caused by: java.lang.IllegalArgumentException: An AuthenticationManager must be set
at org.springframework.util.Assert.notNull(Assert.java:112) ~[spring-core-4.1.6.RELEASE.jar:4.1.6.RELEASE]
at org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter.afterPropertiesSet(AbstractPreAuthenticatedProcessingFilter.java:97) ~[spring-security-web-4.0.1.RELEASE.jar:4.0.1.RELEASE]
I am using Java config, not XML config. My code following the example of How To Inject AuthenticationManager using Java Configuration in a Custom Filter is as follows:
the security configurer adaptor
#Configuration
#EnableWebSecurity
public class MyWebSecurityAdaptor extends WebSecurityConfigurerAdapter {
...
#Bean(name = "myAuthenticationManager")
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
The filter class itself:
#Component
public class MyPreauthFilter extends AbstractPreAuthenticatedProcessingFilter {
...
#Autowired
#Override
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
}
}
If instead of the code in Item 1 above, I try the following:
#Autowired
#Override
protected AuthenticationManager authenticationManager() throws Exception
{
// TODO Auto-generated method stub
return super.authenticationManager();
}
Then the error changes.
It then becomes:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.security.authentication.AuthenticationManager] found for dependency: expected at least 1 bean which qualifies as autowire candidate}
I guess that makes sense, this way does not define a bean. But then why didn't the original way, which DID define a bean, fail?
Instead of adding 'myAuthernticationManager' to the WebSecurityConfigurerAdapter class. Add this directly to you filter class and autowire it.
#Autowired
#Override
public void setMyAuthenticationManager(MyAuthenticationManager myAuthenticationManager) {
this.myAuthenticationManager = myAuthenticationManager;
super.setAuthenticationManager(this.myAuthenticationManager);
}
Remove all the code related to myAuthenticationManager from your WebSecurityConfigurerAdapter.
My application is working well, but I´m not sure why I needed to configure the way I did.
The issue is that transactions must be enabled in both contexts, root and dispatcher.
Why is this necessary?
The app is configured like this:
public class MvcInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses () {
return new Class<?>[] {RootConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses () {
return new Class<?>[] {WebMvcConfig.class};
}
}
Then
#Configuration
#Import(value = {
DataSourceConfig.class,
JpaConfig.class,
SecurityConfig.class
})
#ComponentScan(
basePackages = {"com.**.service", "com.**.mail", "com.core.config"},
excludeFilters = {
#Filter(Configuration.class),
#Filter(Controller.class),
#Filter(RestController.class)
})
#EnableAsync
#EnableScheduling
#EnableAspectJAutoProxy
#EnableTransactionManagement
public class RootConfig {
// Beans Config...
}
Finally
#Configuration
#ComponentScan(basePackages = {"com.web"})
#EnableWebMvc
#EnableTransactionManagement
public class WebMvcConfig extends WebMvcConfigurerAdapter {
// Beans Config ...
}
Note:
Transactions are just managed in the Service Layer.
That #EnableTransactionManagement is present in both config
classes.
If I remove the one in WebMvcConfig, transactions will not be injected in the service layer when accesed from a Controller class. Even though the service layer is scanned by the root context.
If I remove the one in RootConfig, transactions will not be injected in the service layer when accesed for example by Spring Security.
Is there any way to enable transactions globally?
I need to audit invocations of ejb beans. Saying audit I mean write informations such as current logged user, method name, additional description to a database. I decided to do it by use of CDI decorator:
#Decorator
public class AccountServiceBeanDecorator implements AccountService {
#Inject
#Delegate
#Any
AccountService accountService;
#EJB
private AuditService auditService;
#Override
public Account createAccount(Account account) {
auditService.saveAudit("Method: createAccount", currentUser, "Creating account by admin");
return accountService.createAccount(account);
}
}
and the decorated class:
#Stateless
public class AccountServiceBean implements AccountService {
#Override
public Account createAccount(Account account) {
...
}
}
Now if I call AccountService from another ejb stateless bean, what will happen with transaction?:
#Stateless
public ApplicationFacadeBean implements ApplicationFacade {
#EJB
private AccountService accountService;
#Override
public Account createAccount(Account account) {
return accountService.createAccount(account);
}
}
I wanted to log transaction status in decorator (AccountServiceBeanDecorator) and decorated class (AccountServiceBean), so I injected TransactionSynchronizationRegistry as a resource in both classes:
#Decorator
public class AccountServiceBeanDecorator implements AccountService {
#Inject
#Delegate
#Any
AccountService accountService;
#EJB
private AuditService auditService;
#Resource
private TransactionSynchronizationRegistry reg;
#Override
public Account createAccount(Account account) {
log.info("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
log.info("tx ({}): {}", new Object[] {reg.getTransactionStatus(), reg.getTransactionKey()});
log.info("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
auditService.saveAudit("Method: createAccount", currentUser, "Creating account by admin");
return accountService.createAccount(account);
}
}
and
#Stateless
public class AccountServiceBean implements AccountService {
#Resource
private TransactionSynchronizationRegistry reg;
#Override
public Account createAccount(Account account) {
log.info("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
log.info("tx ({}): {}", new Object[] {reg.getTransactionStatus(), reg.getTransactionKey()});
log.info("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
...
}
}
I received strange behavior:
log from decorator
tx (0): JavaEETransactionImpl: txId=6 nonXAResource=null jtsTx=null localTxStatus=0 syncs=[com.sun.ejb.containers.ContainerSynchronization#68fb15d0]]]
NullPointerException on second log (reg is null).
Can anybody explain it to me? Wheter AccountServiceBean class is called within the same transaction as ApplicationFacade?
Thank you
first: i would not mixing ejbs with cdi interceptors. ejbs has it on interceptor implementations.
second: interceptors are executed in the same transaction as the ejb where the interceptor is around.
possible solution:
create a correct ejb interceptor
put the interceptor around the method / class
create a second ejb (MyLoggerBean) with a method like this logToDatabase(String message) and annotate this method with #TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
inside the interceptor create a class member like this: #EJB private MyLoggerBean loggerBean
inside your #AroundInvoke annotated method you could call loggerBean. logToDatabase(...)
this would create a new transaction from inside the current transaction of the ejb where the interceptor is around
--> i know my english is not very good. but i hope that you understand what i think should work. if i have the time, i make e example on github...
Hmm... what container are you using? Generally I wouldn't suspect a CDI decorator to work on an EJB... I can't think of anything in the JEE spec that I've encountered that would give evidence either way.
Faced with your problem though, I did this with an interceptor, not a decorator. These are supported by the EJB spec... Anyway, here's my code, you would need to grab the variables from the context in your case:
import java.lang.reflect.Method;
import javax.inject.Inject;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
public class InvocationCountInterceptor {
#Inject
private InvocationCounter counter;
#AroundInvoke
public Object intercept(InvocationContext ctx) throws Exception {
Object returnValue = ctx.proceed();
Class<? extends Object> className = ctx.getTarget().getClass();
Method methodName = ctx.getMethod();
counter.incrementCounter(className, methodName);
return returnValue;
}
}
Then whatever EJB or EJB Method you want to audit, I just added this: #Interceptors(InvocationCountInterceptor.class)
Anyone has an example on how to use Freemarker WebappTemplateLoader in FreemarkerConfigurer?
I am using Freemarker with Spring MVC and extending the FreeMarkerConfigurer to add various template loaders and I would also like to add a web app loader to load templates in web app context. But I do not know how to get the servletcontext parameter for its constructor.
public class DesktopFreeMarkerConfigurer extends FreeMarkerConfigurer{
#Override
protected void postProcessConfiguration(Configuration config){
[...]
/* Get templates from the webapp/servlet context */
WebappTemplateLoader watl = new WebappTemplateLoader(<servletContext>, "default/ftl/");
[...]
}
}
I would like to add webapp/default/ftl to template loading path, but as it may be dynamic/configurable, I cannot hardcode it in the xml files.
Any suggestions would be greatly appreciated.
Thank you
Carmen
I assume you are defining DesktopFreeMarkerConfigurer as a spring bean.
In that case, it should be simple to get the servlet context.
Just define this in the DesktopFreeMarkerConfigurer class :
#Autowired private ServletContext context;
Or if you choose, you can also make it implements ServletContextAware :
public class DesktopFreeMarkerConfigurer extends FreeMarkerConfigurer implements ServletContextAware {
private ServletContext servletContext;
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
#Override
protected void postProcessConfiguration(Configuration config){
WebappTemplateLoader watl = new WebappTemplateLoader(this.servletContext, "default/ftl/");
...
}
...
}