We're building a web application and after the HttpServer is started I'm trying to load some items from the database into memory. The best way I know how to utilize the service layer for this is to use the following class:
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public void setApplicationContext (ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}
It is continuously null.
public static void main(String[] args) throws IOException {
final HttpServer server = HttpServer.createSimpleServer(".", 8181);
WebappContext ctx = new WebappContext("Socket", "/");
//enable annotation configuration
ctx.addContextInitParameter("contextClass", "org.springframework.web.context.support.AnnotationConfigWebApplicationContext");
ctx.addContextInitParameter("contextLocation", "com.production");
//allow spring to do all of it's stuff
ctx.addListener("org.springframework.web.context.ContextLoaderListener");
//serve static assets
StaticHttpHandler staticHttpHandler = new StaticHttpHandler("src/main/web");
server.getServerConfiguration().addHttpHandler(staticHttpHandler, "/");
//deploy
ctx.deploy(server);
//NULL
WorkflowService workflowService = (WorkflowService) ApplicationContextProvider.getApplicationContext().getBean("workflowService");
server.start();
The service classes are annotated with #Service and my configuration class is...
#Configuration
#ComponentScan(basePackages = {
"com.production"
})
#PropertySource(value= {
"classpath:/application.properties",
"classpath:/environment-${MYAPP_ENVIRONMENT}.properties"
})
#EnableJpaRepositories("com.fettergroup.production.repositories")
#EnableTransactionManagement
public class Config {
...
#Bean
public ApplicationContextProvider applicationContextProvider() {
return new ApplicationContextProvider();
}
}
I believe something about this application using the grizzly WebContext is what is at the root of the issue. But I'm unsure what to do about it. I've done some Googling and it seems the solution I'm using should work...
contextLocation Init Parameter should be contextConfigLocation
Related
I searched all over the stackOverFlow and couldn't find an answer.
I need to start RabbitListener on command - from just REST send request to start this listener (no other option).
So I found that I need set properties in Listener like that: #RabbitListener(queues = "myQueue", id = "listener_id", autoStartup = "false") (of course is albo annotation #Component).
I made also initializer class using implementation of ApplicationContextAware and RabbitListenerEndpointRegistry like below:
#Slf4j
#Component
public class ListenerInitializer implements ApplicationContextAware {
private ApplicationContext applicationContext;
public void startListener() {
RabbitListenerEndpointRegistry listenerRegistry = applicationContext.getBean(RabbitListenerEndpointRegistry.class);
listenerRegistry.getListenerContainer("listener_id").start();
log.info("Listener started.");
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
And when I tried to use method startListener() mostly I received NullPointerException: null like RabbitListenerEndpointRegistry doesn't exist. I wrote mostly, because sometimes (every time I make mvn clean install) it works and everything is fine. But mostly I received NullPointerException.
With #Autowired of ApplicationContext same situation.
I tried with:
#Autowired RabbitListenerEndpointRegistry rabbitListenerEndpointRegistry
but in that case shows me that RabbitListenerEndpointRegistry bean doesn't exist (also through #RequiedArgsConstructor.
I tried also to make configuration like below:
#Configuration
#EnableRabbit
class ListenerConfig {
#Bean
#DependsOn("listener")
ListenerInitializer ListenerInitializer() {
return new ListenerInitializer();
}
#Bean(name = "listener")
Listener listener() {
return new listener();
}
}
and also doesn't work.
Anybody have any idea how I can fix it? I guess I need to initialize injection of ApplicationContext into ListenerInitializer as late as possible due to need to initialize itself RabbitListenerEndpointRegistry, but how to do it?
Many thanks for any advice
edit:
I call it to start through controller
#RequiredArgsConstructor
#RestController
class RestController {
private final ListenerInitializer listenerInitializer;
#GetMapping("/startListener")
ResponseEntity<String> startListener() {
listenerInitializer.startListener();
return ResponseEntity.ok("Listener started.");
}
}
I don't know if this is going to help you somehow, but here is a fully working Spring Boot application:
#SpringBootApplication
public class So75236736Application {
public static void main(String[] args) {
SpringApplication.run(So75236736Application.class, args);
}
#Bean
Listener listener() {
return new Listener();
}
public static class Listener {
#RabbitListener(queuesToDeclare = #Queue("myQueue"), id = "listener_id", autoStartup = "false")
void handle(Object body) {
System.out.println("Received: " + body);
}
}
#Component
public static class ListenerInitializer implements ApplicationContextAware {
private ApplicationContext applicationContext;
public void startListener() {
RabbitListenerEndpointRegistry listenerRegistry =
this.applicationContext.getBean(RabbitListenerEndpointRegistry.class);
listenerRegistry.getListenerContainer("listener_id").start();
System.out.println("Listener started");
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}
#RestController
public static class StartListenerController {
private final ListenerInitializer listenerInitializer;
public StartListenerController(ListenerInitializer listenerInitializer) {
this.listenerInitializer = listenerInitializer;
}
#GetMapping("/startListener")
ResponseEntity<String> startListener() {
this.listenerInitializer.startListener();
return ResponseEntity.ok("Listener started.");
}
}
}
Only difference that I don't use an #EnableRabbit explicitly, but rather rely on the auto-configuration. I also don't declare ListenerInitializer as a #Bean since it is already marked with #Component and that is enough for Spring Boot to scan it.
Ok, I found the solution and sorry for bother you guys. Lack of knowledge. Still I don't know how it possible it works before, because I didn't changed anything, cause I was sure it should be like that, but solution was to move annotation #RabbitListener(queues = "myQueue2", id = "listener_id", autoStartup = "false") from over a class to over a method or add #RabbitHandler over a method.
Thank you #Artem Bilan for your help!
Here is my code:
public class Main {
public static void main(String[] args) {
Main p = new Main();
p.start(args);
}
#Autowired
private MyBean myBean;
private void start(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("META-INF/config.xml");
System.out.println("my beans method: " + myBean.getStr());
}
}
#Service
public class MyBean {
public String getStr() {
return "string";
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config />
<context:component-scan base-package="mypackage"/>
</beans>
Why doesn't this work? I get NullPointerException. Is it possible to use autowiring in a standalone application?
Spring works in standalone application. You are using the wrong way to create a spring bean. The correct way to do it like this:
#Component
public class Main {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("META-INF/config.xml");
Main p = context.getBean(Main.class);
p.start(args);
}
#Autowired
private MyBean myBean;
private void start(String[] args) {
System.out.println("my beans method: " + myBean.getStr());
}
}
#Service
public class MyBean {
public String getStr() {
return "string";
}
}
In the first case (the one in the question), you are creating the object by yourself, rather than getting it from the Spring context. So Spring does not get a chance to Autowire the dependencies (which causes the NullPointerException).
In the second case (the one in this answer), you get the bean from the Spring context and hence it is Spring managed and Spring takes care of autowiring.
Spring is moving away from XML files and uses annotations heavily. The following example is a simple standalone Spring application which uses annotation instead of XML files.
package com.zetcode.bean;
import org.springframework.stereotype.Component;
#Component
public class Message {
private String message = "Hello there!";
public void setMessage(String message){
this.message = message;
}
public String getMessage(){
return message;
}
}
This is a simple bean. It is decorated with the #Component annotation for auto-detection by Spring container.
package com.zetcode.main;
import com.zetcode.bean.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
#ComponentScan(basePackages = "com.zetcode")
public class Application {
public static void main(String[] args) {
ApplicationContext context
= new AnnotationConfigApplicationContext(Application.class);
Application p = context.getBean(Application.class);
p.start();
}
#Autowired
private Message message;
private void start() {
System.out.println("Message: " + message.getMessage());
}
}
This is the main Application class. The #ComponentScan annotation searches for components. The #Autowired annotation injects the bean into the message variable. The AnnotationConfigApplicationContext is used to create the Spring application context.
My Standalone Spring tutorial shows how to create a standalone Spring application with both XML and annotations.
For Spring 4, using Spring Boot we can have the following example without using the anti-pattern of getting the Bean from the ApplicationContext directly:
package com.yourproject;
#SpringBootApplication
public class TestBed implements CommandLineRunner {
private MyService myService;
#Autowired
public TestBed(MyService myService){
this.myService = myService;
}
public static void main(String... args) {
SpringApplication.run(TestBed.class, args);
}
#Override
public void run(String... strings) throws Exception {
System.out.println("myService: " + MyService );
}
}
#Service
public class MyService{
public String getSomething() {
return "something";
}
}
Make sure that all your injected services are under com.yourproject or its subpackages.
I case you are running SpringBoot:
I just had the same problem, that I could not Autowire one of my services from the static main method.
See below an approach in case you are relying on SpringApplication.run:
#SpringBootApplication
public class PricingOnlineApplication {
#Autowired
OrchestratorService orchestratorService;
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(PricingOnlineApplication.class, args);
PricingOnlineApplication application = context.getBean(PricingOnlineApplication.class);
application.start();
}
private void start() {
orchestratorService.performPricingRequest(null);
}
}
I noticed that SpringApplication.run returns a context which can be used similar to the above described approaches. From there, it is exactly the same as above ;-)
A nice solution would be to do following,
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
#Component
public class SpringContext implements ApplicationContextAware {
private static ApplicationContext context;
/**
* Returns the Spring managed bean instance of the given class type if it exists.
* Returns null otherwise.
* #param beanClass
* #return
*/
public static <T extends Object> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
#Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
// store ApplicationContext reference to access required beans later on
SpringContext.context = context;
}
}
Then you can use it like:
YourClass yourClass = SpringContext.getBean(YourClass.class);
I found this very nice solution in the following website: https://confluence.jaytaala.com/pages/viewpage.action?pageId=18579463
I have application with spring mvc annotation configuration. In my local environment(IDE) it is working perfectly. When i deployed in prod(tomcat 8.5) my application listener calling multiple times. Because of this my executor tasks, schedulers calling multiple times.
AppWebAppInitializer.java
package com.app.config;
public class AppWebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(AppMvcConfig.class);
servletContext.addListener(new ContextLoaderListener(ctx));
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("appServlet",new DispatcherServlet(ctx));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/*");
dispatcher.setAsyncSupported(true);
servletContext.setSessionTrackingModes(EnumSet.of(SessionTrackingMode.COOKIE)); //uses cookies instead of jsessionId in the url.
}
}
AppMvcConfig.java
package com.app.config;
#Configuration
#EnableWebMvc
#EnableScheduling
#EnableCaching
#ComponentScan("com.app")
#Import({
AppPropertyInitConfig.class,
AppSecurityConfig.class,
WebSocketConfig.class
})
public class AppMvcConfig extends WebMvcConfigurerAdapter implements ApplicationContextAware{
private ApplicationContext applicationContext;
#Value("${app.upload.dir}")
private String uploadDir;
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
ApplicationStartupListener.java
package com.app.listener;
#Component
public class ApplicationStartupListener {
private final Logger logger = LoggerFactory.getLogger(ApplicationStartupListener.class);
#Autowired
private LookupHolder lookupHolder;
#EventListener
public void handleContextRefreshEvent(ContextRefreshedEvent event) {
logger.info("ApplicationStartupListener initializing lookups");
Runnable lookupInitRunnable = () -> lookupHolder.init(); //initialize the lookups
new Thread(lookupInitRunnable).start();
}
}
In above code ApplicationStartupListener.java --> handleContextRefreshEvent() calling multiple times. As well as my spring default schedules also calling multiple times
In my application i don't have web.xml
I have spring boot, hibernate application and android application for client side. Also I am using java.net.Socket api for socket connection.
Before I was creating server socket like this new Server(12346); and everything was good enough. But now I need access to database from socket class e.g. with #Autowired UsersDao field, but of course it is null because Socket class is not visible by Spring Framework.
So how do I make dependency injection on Socket class using port as constructor argument and make UserDao non-null?
You can access the Spring Application Context from static method and use this static method to load your repository bean in your Server class instead of autowiring it.
You need to create the following classes (found here):
ApplicationContextProvider
#Component
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext context;
public ApplicationContext getApplicationContext() {
return context;
}
#Override
public void setApplicationContext(ApplicationContext ctx) {
context = ctx;
}
}
SpringConfiguration
#Configuration
public class SpringConfiguration {
#Bean
public static ApplicationContextProvider contextProvider() {
return new ApplicationContextProvider();
}
}
And then your non-Spring managed Server class:
public class Server {
//your code
public void doUsersDaoStuff() {
UsersDao usersDao = (UsersDao) SpringConfiguration.contextProvider().getApplicationContext().getBean("UsersDao");
// Do your own stuff with UsersDao here...
}
}
For the past 2 weeks things have been going great in my application. Last night I login remotely to work to find out that when I run my application my ApplicationContextProvider class no longer has knowledge of the Application Context. I've run Maven clean & build in addition to rebooting my PC. Can't seem to shake it...
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public void setApplicationContext (ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}
My Main class:
public static void main(String[] args) throws IOException {
System.setProperty("java.util.logging.SimpleFormatter.format", "%4$s: %5$s%n");
final HttpServer server = HttpServer.createSimpleServer(".", 80);
WebappContext ctx = new WebappContext("ProductionQueue", "/");
//enable annotation configuration
ctx.addContextInitParameter("contextClass", "org.springframework.web.context.support.AnnotationConfigWebApplicationContext");
ctx.addContextInitParameter("contextConfigLocation", "com.production");
//allow spring to do all of it's stuff
ctx.addListener("org.springframework.web.context.ContextLoaderListener");
....
ctx.deploy(server);
server.start();
//start the production process
Production.init();
System.in.read();
server.stop();
My Production class:
public class Production {
private static final Logger logger = Logger.getLogger(Production.class.getName());
/* A list of active workflows */
private static List<Workflow> workflowList = new ArrayList<Workflow>();
private static ProductionService productionService;
/**
* Initialize the production line
*/
public static void init() {
logger.info("Initializing production workflows...");
ApplicationContext context = ApplicationContextProvider.getApplicationContext(); //THIS IS NULL
productionService = (ProductionService) context.getBean("productionService");
No configuration has been modified at all. Within my config class I do have a bean for it...
#Configuration
#ComponentScan(basePackages = {
"com.production"
})
#PropertySource(value= {
"classpath:/application.properties",
"classpath:/environment-${FETTER_ENVIRONMENT}.properties"
})
#EnableJpaRepositories("com.production.repository")
#EnableTransactionManagement
public class Config {
#Value("${db.url}")
String PROPERTY_DATABASE_URL;
#Value("${db.user}")
String PROPERTY_DATABASE_USER;
#Value("${db.password}")
String PROPERTY_DATABASE_PASSWORD;
#Value("${persistenceUnit.default}")
String PROPERTY_DEFAULT_PERSISTENCE_UNIT;
#Value("${hibernate.dialect}")
String PROPERTY_HIBERNATE_DIALECT;
#Value("${hibernate.format_sql}")
String PROPERTY_HIBERNATE_FORMAT_SQL;
#Value("${hibernate.show_sql}")
String PROPERTY_HIBERNATE_SHOW_SQL;
#Value("${entitymanager.packages.to.scan}")
String PROPERTY_ENTITYMANAGER_PACKAGES_TO_SCAN;
#Bean
public ApplicationContextProvider applicationContextProvider() {
return new ApplicationContextProvider();
}
I'd say its mudsoup between the try to have it static and use it as a bean.
You creating a new instance of the ApplicationContextProvider as a spring bean. This is ApplicationContextAware and so gets the AC injected. But THEN you do not use said bean, you use its static getter to read the field, yet this, static thing never received the AC in the first place. You're never using your actual bean.
I'd say scratch that provider completly, and rely soley on the ApplicationContextAware interface, it does what you want, ie it was designed to do exactly that, why use a delegating bean?
I do not know if
#Bean
public ApplicationContextProvider applicationContextProvider() {
return new ApplicationContextProvider();
}
the ApplicationContextAware interface.
Try to add #Component at ApplicationContextProvider class and then remove the #Bean. I hope that the ApplicationContextAware` interface is taken in account if this class is found by your normal component scan.
Turns out there was a buried exception my logging was preventing me from getting access to. Thanks for help.