Spring bean wiring in different contexts - java

For datasource layer I use the following Spring Configuration file:
#Configuration
#ComponentScan(basePackages = {"com.savdev.springmvcexample.repository", "com.savdev.springmvcexample.config"})
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = {"com.savdev.springmvcexample.repository"})
public class InfrastructureContextConfiguration {
...
#Configuration
#Profile(value = "file_based")
#PropertySource("classpath:/db/config/file_based.properties")
public static class FileBasedConfiguration {
#Inject
private Environment environment;
#Bean
public DataSource dataSource() {
BasicDataSource dataSource = new org.apache.commons.dbcp.BasicDataSource();
dataSource.setDriverClassName(environment.getProperty("jdbc.driver"));
dataSource.setUrl(environment.getProperty("jdbc.url"));
dataSource.setUsername(environment.getProperty("jdbc.username"));
dataSource.setPassword(environment.getProperty("jdbc.password"));
return dataSource;
}
}
...
To run tests I load this configuration via #ContextConfiguration:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { InfrastructureContextConfiguration.class, HsqldbEmbeddableDbStarterContextConfiguration.class })
#TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = false)
#Transactional()
#ActiveProfiles(profiles = {"file_based", "test_data"} )
public abstract class AbstractJpaJavaTestBase {
...
And it works fine.
The same InfrastructureContextConfiguration class is used in web module when DispatcherServlet is created:
public class SpringMvcExampleWebApplicationInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
registerDispatcherServlet(servletContext);
}
private void registerDispatcherServlet(final ServletContext servletContext) {
WebApplicationContext dispatcherContext = createContext(WebMvcContextConfiguration.class, InfrastructureContextConfiguration.class);
DispatcherServlet dispatcherServlet = new DispatcherServlet(dispatcherContext);
dispatcherServlet.setContextInitializers( new SpringMvcExampleProfilesInitializer());
ServletRegistration.Dynamic dispatcher;
dispatcher = servletContext.addServlet("dispatcher", dispatcherServlet);
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
private WebApplicationContext createContext(final Class<?>... annotatedClasses) {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(annotatedClasses);
return context;
}
}
But now, I'm getting NullPointerException in the following line of InfrastructureContextConfiguration:
dataSource.setDriverClassName(environment.getProperty("jdbc.driver"));
The environment is not wired. What can I do to resolve it?

What I found. The similar issue already have been met:
same1, some solutions
seems the problem is not connected, but the last answer is the best solution
total:
Actually, the field that is injected with #Inject cannot be null. It must throws exception. As a result if it is null then - the annotation has not been applied at all. As a result the main reason is the absence of its implementation in classpath.
So I added the following in my web.pom. and it resolved the problem:
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
As alternative options I could use:
#Resource instead of #Inject, and environment has been set.
Passed the envirionment as argument into the constructor, instead of wiring it via annotation. But the best case, IMHO, is fix jar dependecy.

Related

Configuration class doesn't run when I add #Autowired WebService field

I have a setup Spring + Apache CXF and I'm trying to make CustomerWebServiceImpl injectable. To do this I should inject it within #Autowired WebServiceConfig. When I do this, the WebServiceConfig doesn't even run(checked via debugger). In stack trace I see the following exception:
NoSuchBeanDefinitionException: No qualifying bean of type 'com.beginnercourse.softcomputer.domains.customer.CustomerWebService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
I've checked spring annotations, everything marked with #Component/#Service. Made sure that I don't initialize any of these beans with new. Tried to replace field autowiring by setters
I have seen that there could be unexpected problems, like here
My code:
CustomerWebService
#WebService
public interface CustomerWebService {
String addCustomer(CustomerDto newCustomer);
}
CustomerWebServiceImpl
#Component
#WebService(endpointInterface = "com.soft.domains.customer.CustomerWebService")
public class CustomerWebServiceImpl implements CustomerWebService {
#Autowired
private CustomerService customerService;
#Override
public String addCustomer(CustomerDto newCustomer) {
System.out.println("Hello world");
customerService.create(new CustomerEntity());
return "Hello " + newCustomer.getName();
}
}
WebServiceConfig
#Configuration
public class WebServiceConfig {
#Autowired
private CustomerWebService customerWebService;
#Bean(name = Bus.DEFAULT_BUS_ID)
public SpringBus springBus() {
return new SpringBus();
}
#Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(springBus(), customerWebService);
endpoint.publish("http://localhost:8080/services/customer");
return endpoint;
}
}
UPD: I seems like only one of my configuration classes(RootConfig) is initialized fine. Others are not initializing beans. Maybe WebApplicationInitializer.onStartup expect another configuration. Then what kind of startup should be used to run new CXFServlet() with such Configuration classes?
public class WebServiceDispatcherServletInitializer implements WebApplicationInitializer {
private static final String SERVICE_DISPATCHER_SERVLET = "service_dispatcher_servlet";
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(WebServiceConfig.class, RootConfig.class, PersistenceConfig.class);
servletContext.addListener(new ContextLoaderListener(context));
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", new CXFServlet());
dispatcher.addMapping("/services/*");
}
}

Spring Data - #Transactional annotation in Service class raises AopConfigException

I am using Spring and Hibernate and I am successfully autowiring a Repository inside the contructor of a Service class. When i try to add #Transactional methods in my Service class i get an AopConfigException that it is about the generation of CGLib class.
My Configuration consists in 3 files.
An AppInitializer class that implements WebApplicationInitializer , a WebMvcConfig class that extends WebMvcConfigurerAdapter and lastly a PersistentContext class.
AppInitializer
public class AppInitializer implements WebApplicationInitializer {
private static final String CONFIG_LOCATION = "com.project.app.config";
private static final String MAPPING_URL = "/";
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
WebApplicationContext context = getContext();
servletContext.addListener(new ContextLoaderListener(context));
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("DispatcherServlet",
new DispatcherServlet(context));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping(MAPPING_URL);
}
private AnnotationConfigWebApplicationContext getContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setConfigLocation(CONFIG_LOCATION);
return context;
}
WebMvcConfig
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = { "com.project.app" })
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Autowired
private Environment env;
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("hello");
}
#Bean
public ApplicationContextProvider applicationContextProvider() {
return new ApplicationContextProvider();
}
}
PersistentContext
#Component
#EnableJpaRepositories("com.project.app.services.repositories")
#EnableTransactionManagement
#PropertySource("classpath:application.properties")
public class PersistenceContext {
#Autowired
private Environment env;
#Bean
#Primary
public DataSource dataSource() throws ClassNotFoundException {
DataSource ds = new DataSource();
ds.setUrl(env.getProperty(SystemSettings.DS_URL));
ds.setUsername(env.getProperty(SystemSettings.DS_USERNAME));
ds.setPassword(env.getProperty(SystemSettings.DS_PASSWORD));
ds.setDriverClassName(env.getProperty(SystemSettings.DS_DRIVER));
return ds;
}
#Bean
LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource);
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
entityManagerFactoryBean.setPackagesToScan("com.project.app.services.entities");
// .. Set Properties..
entityManagerFactoryBean.setJpaProperties(jpaProperties);
return entityManagerFactoryBean;
}
#Bean
JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
}
The Repository INTERFACE extends CrudRepository
StopRepository
#Repository
#RepositoryRestResource(collectionResourceRel = "stop", path = "stop")
public interface StopRepository extends CrudRepository<StopJPA, Long> {
#Override
StopJPA save(StopJPA persisted);
}
The Entity class.
StopJPA
#Entity
#Table(name = "stop")
public class StopJPA implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "stop_description")
private String stopDescription;
#Column(name = "id_stop", nullable = false)
private String idStop;
public StopJPA() {
}
public StopJPA(String stopDescription, String idStop) {
this.stopDescription = stopDescription;
this.idStop = idStop;
}
// .. Getters & Setters ..
}
And the Service class implementation:
StopService
#Service
final class RepoStopService {
private StopRepository stopRepository;
#Autowired
RepoStopService(StopRepository stopRepository) {
this.stopRepository = stopRepository;
}
#Transactional
public StopDTO create(StopDTO newEntry) {
StopJPA created = new StopJPA(newEntry.getStopDescription(), newEntry.getIdStop());
created = stopRepository.save(created);
return EntitiesConverter.mapEntityIntoDTO(created);
}
}
Unfortunately when i try to run it on server i get this exception:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'repoStopService' defined in file [C:..\RepoStopService.class]: Initialization of bean failed;
Caused by: org.springframework.aop.framework.AopConfigException:Could not generate CGLIB subclass of class [class ..RepoStopService]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Cannot subclass final class class ..RepoStopService
I understand that Spring uses either JDK proxy or CGLib. The default behavior if we autowire an interface (here its the repository) is to have a JDK proxy?
what need to be changed in order to make it work?
The exception message is quite clear
AopConfigException:Could not generate CGLIB subclass of class [class ..RepoStopService
First of all it is complaining about the fact that it cannot create a proxy for your service it doesn't mention your repositories at all. Your service is a class without an interface and it is also final.
The #Transactional is on a method in your service. Spring needs to create a proxy for your service to be able to start and commit the transaction at that point. As your service isn't implementing an interface it tries to create a class based proxy, however it cannot because your class is marked final.
To fix, remove the final or create an interface to be able to use JDK Dynamic proxies instead of Cglib based class proxies.
Note: Depending on the version of Spring used, it will still fail when removing the final keyword as on earlier versions it is also required to have a no-arg default constructor and you only have a single argument constructor.

EntityManager injection in Tomcat

I'm trying to write a web application that will run on Tomcat 8 using Spring MVC 4, Hibernate and JPA.
I'm doing this annotation driven so no XML in the Spring code if I can help it.
I have several layers in the application: Controllers that are part of the Spring MVC web app; services and repositories (although these are not annotated as Spring services etc.), all are in different JAR files that are distributed in the web app's WAR.
I'd like to inject either EntityManager or EntityManagerFactory into my code (I'm using a variation of the repository pattern) but can't get it to work.
I also have a persistence.xml defined that sets up a persistence unit.
I've tried several things and nothing works.
I've added #PersistenceContext to both an EntityManager and EntityManagerFactory variable;
I've tried #PersistenceUnit in the repository code but no luck.
The last thing I tried (following this post Injecting EntityManager with a producer in tomcat) was to add a listener to the web app and use #Inject in my code. The listener is created but the 'createEntityManager' method is never called (and yes, I've changed it to add the #Produces annotation)
So my listener looks like this:
#Produces
public EntityManager createEntityManager() {
System.out.println("*********** EntityManagerFactoryListener - createEntityManager: ");
if (entityManagerFactory == null) {
throw new IllegalStateException("Context is not initialized yet.");
}
return entityManagerFactory.createEntityManager();
}
And my reoository looks like this
#Inject
public void setEntityManager(final EntityManager entityManager) {
this.entityManager = entityManager;
System.out.println("*********** entityManager: " + entityManager);
}
The error I get is
Error creating bean with name 'hibernateRepository': Injection of autowired dependencies failed
...
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManager] ...
Am I missing something? Can I even do this in Tomcat or do I need an EJB container?
EDIT
In answer to the question I have a WebApplictionInitializer that looks like this:
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
WebApplicationContext context = getContext();
servletContext.addListener(new ContextLoaderListener(context));
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("DispatcherServlet", new DispatcherServlet(context) );
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
private WebApplicationContext getContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(WebConfig.class);
return context;
}
and a WebMvcConfigurerAdapter that looks like:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.pluralsight")
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
#Bean
public InternalResourceViewResolver getInternalResourceViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/jsp/");
resolver.setSuffix(".jsp");
return resolver;
}
}

Can not assign ServletContext spring 3

I am trying to use servletcontext.getRealPath in my Util class to load a file resource (Not part of unit testing) but it does not work.
I tried both to use "implements ServletContextAware":
#Component
public class Utils implements ServletContextAware{
private ServletContext servletContext;
#Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
System.out.println("**** "+servletContext);
}
}
Which throws NPE since servletcontext is not assigned by spring.
And the #Autowired route:
#Component
public class Utils{
#Autowired
private ServletContext servletContext;
Which throws NoSuchBeanDefinitionException when tomcat is being starting:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [javax.servlet.ServletContext] found for dependency: expected at le
ast 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
I am adding the my initialization code in case I am doing something wrong which prevents Spring to inject the right bean.
public class WebAppInitializer implements WebApplicationInitializer {
private static Logger LOG = LoggerFactory.getLogger(WebAppInitializer.class);
#Override
public void onStartup(ServletContext servletContext) {
WebApplicationContext rootContext = createRootContext(servletContext);
configureSpringMvc(servletContext, rootContext);
FilterRegistration.Dynamic corsFilter = servletContext.addFilter("corsFilter", CORSFilter.class);
corsFilter.addMappingForUrlPatterns(null, false, "/*");
// configureSpringSecurity(servletContext, rootContext);
}
private WebApplicationContext createRootContext(ServletContext servletContext) {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
// rootContext.register(CoreConfig.class, SecurityConfig.class);
rootContext.register(CoreConfig.class);
rootContext.refresh();
servletContext.addListener(new ContextLoaderListener(rootContext));
servletContext.setInitParameter("defaultHtmlEscape", "true");
return rootContext;
}
CoreConfig.class:
#Configuration
public class CoreConfig {
#Bean
public CaptionFixture createCaptionFixture() {
return new CaptionFixture();
}
#Bean
public Utils createUtils () {
return new Utils();
}
}
Utils is the class with the servlet context.
I have looked at the suggested answers: here and here and it didnt work.
The issue is that you are calling refresh() without a ServletContext being registered, so none is available when the beans are initialized.
Get rid of this call
rootContext.refresh();
The ContextLoaderListener will take care of calling refresh(). The constructor javadoc explains what happens when the ApplicationContext passed as an argument isn't refreshed.

Autowired property is null - Spring Boot Configuration

I am stuck with null values in an autowired property. I am hoping I could get some help.
We are using for the project spring-boot version 0.5.0.M6.
The four configuration files with beans are in one package and are sorted by "area":
Data source configuration
Global method security configuration (as we use Spring-ACL)
MVC configuration
Spring Security configuration
The main method that bootstraps everything is in the following file:
#EnableAspectJAutoProxy
#EnableSpringConfigured
#EnableAutoConfiguration(exclude = {
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class,
JpaRepositoriesAutoConfiguration.class,
SecurityAutoConfiguration.class,
ThymeleafAutoConfiguration.class,
ErrorMvcAutoConfiguration.class,
MessageSourceAutoConfiguration.class,
WebSocketAutoConfiguration.class
})
#Configuration
#ComponentScan
public class IntegrationsImcApplication {
public static void main(String[] args) throws Exception {
ApplicationContext ctx = SpringApplication.run(
IntegrationsImcApplication.c lass, args);
}
}
The first file that holds the data source configuration beans is as follows (I have omitted some method body parts to make it more readable):
#EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
#Configuration
public class RootDataSourceConfig
extends TomcatDataSourceConfiguration
implements TransactionManagementConfigurer {
#Override
public DataSource dataSource() {
return jpaDataSource();
}
public PlatformTransactionManager annotationDrivenTransactionManager() {
return jpaTransactionManager();
}
#Bean
public HibernateExceptionTranslator hibernateExceptionTranslator() {
return new HibernateExceptionTranslator();
}
#Bean(name="jpaDataSource")
public DataSource jpaDataSource() {......}
#Bean(name = {"transactionManager","txMgr"})
public JpaTransactionManager jpaTransactionManager() {......}
#Bean(name = "entityManagerFactory")
public EntityManagerFactory jpaEmf() {......}
}
And here is the next configuration file, that depends on the data source from above. It has about 20 beans related to ACL configuration, but it fails on the firsts bean that uses data source:
#EnableGlobalMethodSecurity(prePostEnabled = true)
#Configuration
public class RootGlobalMethodSecurityConfig
extends GlobalMethodSecurityConfiguration
implements Ordered {
#Autowired
public DataSource dataSource;
#Override
public int getOrder() {
return IntegrationsImcApplication.ROOT_METHOD_SECURITY_CO NFIG_ORDER;
}
#Bean
public MutableAclService aclService()
throws CacheException, IOException {
MutableJdbcAclService aclService = new MutableJdbcAclService(
dataSource, aclLookupStrategy(), aclCache());
aclService.setClassIdentityQuery("SELECT ##IDENTITY");
aclService.setSidIdentityQuery("SELECT ##IDENTITY");
return aclService;
}
...................................
}
Basically invoking aclService() throws an error as dataSource is null. We have tried ordering the configuration files by implementing the Ordered interface. We also tried using #AutoConfigureAfter(RootDataSourceConfig.class) but this did not help either. Instead of doing #Autowired on the DataSource we also tried injecting the RootDataSourceConfig class itself, but it was still null. We tried using #DependsOn and #Ordered on those beans but again no success. It seems like nothing can be injected into this configuration.
The console output at the startup is listing the beans in the order we want them, with data source being the first. We are pretty much blocked by this.
Is there anything weird or unique we are doing here that is not working? If this is as designed, then how could we inject data source differently?
Repo: github
Eager initialization of a bean that depends on a DataSource is definitely the problem. The root cause is nothing to do with Spring Boot or autoconfiguration, but rather plain old-fashioned chicken and egg - method security is applied via an aspect which is wrapped around your business beans by a BeanPostProcessor. A bean can only be post processed by something that is initialized very early. In this case it is too early to have the DataSource injected (actually the #Configuration class that needs the DataSource is instantiated too early to be wrapped properly in the #Configuration processing machinery, so it cannot be autowired). My proposal (which only gets you to the same point with the missing AuthenticationManager) is to declare the GlobalMethodSecurityConfiguration as a nested class instead of the one that the DataSource is needed in:
#EnableGlobalMethodSecurity(prePostEnabled = true)
#Configuration
protected static class ActualMethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
#Autowired
#Qualifier("aclDaoAuthenticationProvider")
private AuthenticationProvider aclDaoAuthenticationProvider;
#Autowired
#Qualifier("aclAnonymousAuthenticationProvider")
private AnonymousAuthenticationProvider aclAnonymousAuthenticationProvider;
#Autowired
#Qualifier("aclExpressionHandler")
private MethodSecurityExpressionHandler aclExpressionHandler;
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.authenticationProvider(aclDaoAuthenticationProvider);
auth.authenticationProvider(aclAnonymousAuthenticationProvider);
}
#Override
public MethodSecurityExpressionHandler createExpressionHandler() {
return aclExpressionHandler;
}
}
i.e. stick that inside the RootMethodSecurityConfiguration and remove the #EnableGlobalMethodSecurity annotation from that class.
I might have resolved the problem.
GlobalMethodSecurityConfiguration.class has the following setter that tries to autowire permission evaluators:
#Autowired(required = false)
public void setPermissionEvaluator(List<PermissionEvaluator> permissionEvaluators) {
....
}
And in my case the aclPermissionEvaluator() bean needs aclService() bean, which in turn depends on another autowired property: dataSource. Which seems not to be autowired yet.
To fix this I implemented BeanFactoryAware and get dataSource from beanFactory instead:
public class RootMethodSecurityConfiguration extends GlobalMethodSecurityConfiguration implements BeanFactoryAware {
private DataSource dataSource;
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.dataSource = beanFactory.getBean("dataSource", DataSource.class);
}
....
}
After this, other exception showed up, whereSecurityAutoConfiguration.class is complaining about missing AuthenticationManager, so I just excluded it from #EnableAutoConfiguration. I am not sure if its ideal, but I have custom security configuration, so this way everything works ok.

Categories