Java Spring - How to set up environment? [duplicate] - java

This question already has answers here:
Running code after Spring Boot starts
(18 answers)
Closed 4 years ago.
I am building an application using Java Spring where I would like to run some environment setup code before my application starts handling requests. In this particular example, I'm using PayPal Rest SDK and I would like to set up some notification webhooks for my application. For obvious reasons I don't want to have an endpoint to call to set up the webhooks after the application is started, so putting it in my controller is probably not a good idea, and I need some Spring configuration information to set it up so I can't put it in main(). I'm ok with (in fact I'd even prefer) the application crashing if the webhooks fail to be created, if that's a constraint that needs to be considered.
What's a good way to do this?
Thanks.

and I need some Spring configuration information to set it up so I can't put it in main()
The above statement is not true. You can access your Spring configuration in a main. Consider the following example.
#SpringBootApplication
public class Main {
#Autowire
private MyService service;
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(InterviewHqApplication.class, args);
ctx.getBean(Main.class).setup();
}
private void setup() {
service.doStuff();
}
}
In this example, the setup() method is called after the application context has loaded.
There are actually several ways to do what you are attempting. Spring boot also supports using ApplicationRunner and CommandLineRunner, which both call a run method after the application context has been loaded an alternative to what I have shown above. You can also listen for an ApplicationReadyEvent and you could call #PostConstruct do perform some specific configuration on a bean after it's initialized.

Related

How to run a SpringApplication with AnnotationConfigWebApplicationContext?

We have a SpringApplication that runs fine with the default ApplicationContext, but we have a scenario in which we need to refresh the context and the default context does not allow us to do this. I've update our main Application class to look like this:
// package and import lines not shown here but are included in original source
#ComponentScan("edge")
#EnableAutoConfiguration
#Configuration
#EnableTransactionManagement
#EnableAsync
#EnableScheduling
public class Application {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(Application.class);
app.setApplicationContextClass(AnnotationConfigWebApplicationContext.class);
app.run(args);
}
With this code as it is, calling app.run(args) results in the following stack trace:
Exception in thread "main" java.lang.IllegalStateException: BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext
at org.springframework.context.support.AbstractRefreshableApplicationContext.getBeanFactory(AbstractRefreshableApplicationContext.java:170)
at org.springframework.boot.context.event.EventPublishingRunListener.registerApplicationEventMulticaster(EventPublishingRunListener.java:70)
at org.springframework.boot.context.event.EventPublishingRunListener.contextPrepared(EventPublishingRunListener.java:65)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:305)
at edge.server.Application.main(Application.java:43)
Stepping through SpringApplication.run(), I noticed that the context's BeanFactory is null. If I remove the line app.setApplicationContextClass(AnnotationConfigWebApplicationContext.class), thereby setting the application to use the default context, the code runs up until the point where we call refresh(). That call results in:
java.lang.IllegalStateException: GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once
Has anyone used AnnotationConfigWebApplicationContext in a SpringApplication in the manner I've described and had it work? If so, do you have any suggestions for getting this to work? I've tried looking for ways to create the BeanFactory manually prior to calling app.run(), but there does not appear to be any public methods for doing that. Thanks in advance for any help provided!
Edit for clarification:
Thanks for the comments and answers thus far. I should have been more explicit in my original post regarding the scenario that requires refreshing the ApplicationContext and my attempt at using AnnotationConfigWebApplicationContext. We have some code that runs after the server starts up that we're using for backup and restore purposes, which involves modifying the content of the JpaRepositories that we're using. My understanding is that after running this code, we need to refresh the ApplicationContext in order to call all of our init methods again.
Attempting to do this with the default context class (AnnotationConfigEmbeddedWebApplicationContext, a subclass of GenericApplicationContext) throws the IllegalStateException that I had mentioned before (GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once). That exception is what prompted my attempt to explicitly set the context to AnnotationConfigWebApplicationContext, which is a subclass of AbstractRefreshableApplicationContext. My assumption was that I needed the application to use an AbstractRefreshableApplicationContext in order to make multiple calls to the refresh method successfully. Is there a way to get the code I have above to work, or is there some alternative approach I should be taking that would allow us to refresh the context multiple times?
You are using Spring Boot which already does this for you, you are just making it overly complex. Change your class to the following.
#EnableAsync
#EnableScheduling
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
And make sure it is in the edge package and make sure you have the spring-boot-starter-web dependency which enables a web application. There is no need to mess around with the context class yourself.

Spring Shell - usage and execution

I want to integrate Spring Shell within a spring boot application. I am able to execute the examples from the official git-repo. But when migrating the example code to my own project that is very very similar to this code, my individual shell is not shown or usable. Instead the default Spring Shell splash is shown is usable:
<SpringShell ASCII-Art>
1.1.0.RELEASE
Welcome to Spring Shell. For assistance press or type "hint" then hit ENTER
spring-shell>
Compilation gives no errors, but the individual sample #component marked classes are not used. All annotations are properly set. A standard loader outside is existent. I am not executing the code in an IDE.
Although the documentation (chapter 3.5) tells, that the components are automatically collected as far as i understood.
So my question is more or less how to setup the usage better than
this:
public class Main {
public static void main(String[] args) throws Exception {
Bootstrap.main(args);
}
}
And to defeat the default splash!
That comment in the documentation is a bit misleading (I'll change it).
For your components to be picked up, they need to be on the classpath AND you'll need to scan for them somehow.
See for example how in the Spring XD project, there is a scan for the org.springframework.xd.shell package. You need to do something similar for your own package.
SOLUTION:
ebottard's answer brought me to the point of creating a "spring-shell-plugin.xml" under resources\META-INF\spring\... Although the component scan was set externally already, this seems to be necessary. The following code shows how to start it up within an Spring Boot Application where CommandLineRunner is implemented. This should bridge starting problems.
#Component
public class CLIBean implements CommandLineRunner {
#Override
public void run(String... args) throws Exception {
Bootstrap bootstrap = new Bootstrap();
bootstrap.run();
}
}

How to run Spring integation tests when using aspects?

I'm developing a Spring MVC web application (Java 6 and Spring 3.0.6 currently).
I'm starting to write some Spring integration tests using Junit4 that extend AbstractTransactionalJUnit4SpringContextTests. I invoke these either through our Maven build or in the EclipseIDE (3.7). These tests invoke Controller methods (i.e., methods annotated with #RequestHandler in a class annotated with #Controller).
All was going well until I added aspect-based logging into the controller :
// public controller methods
#Pointcut("execution(public * com.axiope.webapp.controller.*.*(..))")
private void publicControllerMethod() {
}
#Pointcut("#annotation(org.springframework.web.bind.annotation.RequestMapping)")
private void requestHandler(){}
#Pointcut("publicControllerMethod() && requestHandler() ")
private void controllerHandler(){}
// logs contoller exceptions
#AfterThrowing(
pointcut="controllerHandler()",
throwing="ex")
public void logControllerExceptions(Throwable ex) {
logger = LogFactory.getLog(ex.getClass());
logger.error("Controller exception !" + ex.getMessage());
}
Now when I run tests through Maven I get an error like :
No unique bean of type [com.axiope.webapp.controller.StructuredDocumentController]
is defined: expected single bean but found 0:
In the tests, I'm loading the controller from the applicationContext in the setUp method:
structuredDocumentController = applicationContext.getBean(
StructuredDocumentController.class);
This error doesn't happen if I comment out the aspect. I suspect it has something to do with Spring proxying the controller and then the controller class isn't identifiable by its class name. I've tried declaring the controller as a bean in applicationContext.xml but this doesn't help. This problem also occurs when running the tests in Eclipse, so it's not a problem with my Maven configuration.
My question is : how can I get the controller bean detected in the tests?
Would be really grateful for any help - is it wrong to add aspects to methods in controller classes? Should I disable aspects somehow when testing? (Although ideally I'd like to see in the integration tests that logging is working properly).
Thanks very much
Richard
Aspects are not the likely issue, you should be able to use them in the controller also without any problems.
My guess is that you are not loading up the correct context for your tests - how have you specified the application context to use for this test - is there #ContextConfiguration on your test class with the location, is the location to Root context(one specified through ContextLoaderListener) or Web application Context(one specified via DispatcherServlet).

Spring: how to get hold of Application context in Webapp and Standalone program

I'm new to the Spring Framework. We want to introduce it (3.1) in a web application, currently using struts in the web layer, service facades and business objects in the business layer and self-written JDBC DAOs in the persistence layer (all of it closely coupled!)
I created several .xml configurations, one for the servlet config, scanning the com.mydomain.web package only. Another one on the service layer appContext-service.xml which scans com.mydomain.bs and .bo packages and one for the DAO layer appContext-persistence.xml scanning the .dao package.
We have four Eclipse projects with appropriate project dependencies: Web, Business, Common (contains domain objects, DTOs, Exceptions, etc), DataAccess.
I want to use annotations where possible and already created a MVC controller, a new service with interface and a new dao with interface, using the JDBC template, which all works great.
Now my questions are:
We can't re-write all the code at once, we're talking about a larger code base here. But what do I do, when the newly created service is also needed from services and business objects that are not (yet) Spring aware? They're not beans or not being created by Spring. How would I get hold of my service bean?
We have several standalone applications for batch processing, cleaning up the file system and database tables periodically, etc. They're triggered by cron (UNIX cron) and therefore have their own JVM. How would I best use Spring services here, given the different .xml configurations?
Does my setup make any sense at all?
Thanks for any insight.
It's very common that one let spring handle the lifecycle of all the beans, otherwise it might get a bit tricky. The objects that are not spring beans are hopefully initialized somewhere. Make that initializer a spring bean and make it application context aware
public class SpringContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void init(){
ServiceBean1 srv1 = (ServiceBean1)applicationContext.getBean("serviceBean1");
myNonSpringObject.setService1(srv1); // Or something
}
}
Setting up a standalone spring app is very easy. Just create a Spring XML and wire your beans (either via scanning/annotations or XML). It is not really recommended to do this in the main method, but you could easily figure out how to get this setup in your standalone application. Keep in mind that your application itself should not really do much lifecycle logic but let Spring do that.
public class StandaloneSpringApp{
public static void main(String[] args){
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
SomeBeanType bean = (SomeBeanType)ctx.getBean("SomeBeanName");
bean.doProcessing(); // or whatever
}
}
Your setup makes perfect sense, even though I cannot visualize your entire scope, your approach is a good starting point for a large modularized spring application.

Designing a Java library with Spring

I am extracting some functionality from an existing program into a separate library.
This program uses Spring for dependency injection and other tasks and I'd like to keep using it in the library as well.
This library needs to monitor the filesystem for changes, so it will kick off some kind of separate thread to do this.
I don't really know what my options are for initialisation of the library:
How do I initialise the library's context? I cannot assume that library users will make use of Spring too, but I can distribute Spring with the library.
How do I manage my filesystem monitoring thread? Is it good design to expect the program to instantiate a main class of the library and the call init or something like that?
How do I initialise the library's context? I cannot assume that
library users will make use of Spring too, but I can distribute Spring
with the library.
I am writing a library using Spring context as well and I did something like that, assuming your library is called FooLib, has two services called FooService and BarService and a class called SpringContext that configures your spring context through java config:
public final class FooLib {
private static ApplicationContext applicationContext;
private FooLib() {
}
public static FooService getFooService() {
return getApplicationContext().getBean(FooService.class);
}
public static BarService getBarService() {
return getApplicationContext().getBean(BarService.class);
}
private static ApplicationContext getApplicationContext() {
if (applicationContext == null) {
applicationContext = new AnnotationConfigApplicationContext(SpringContext.class);
}
return applicationContext;
}
}
Then a client can use BarService this way:
BarService barService = FooLib.getBarService();
How do I manage my filesystem monitoring thread? Is it good design to
expect the program to instantiate a main class of the library and the
call init or something like that?
You can start your monitoring subsystem statically within Spring context, inside the SpringContext class, for example.
#Configuration
#ComponentScan(basePackages = "com.yourgroupid.foolib")
public class SpringContext {
#Bean
public MonitoringSystem monitoringSystem() {
MonitoringSystem monitoringSystem = new MonitoringSystem();
monitoringSystem.start();
return monitoringSystem;
}
}
That should be enough because Spring creates its beans eagerly by default.
Cheers
How do I initialise the library's context? I cannot assume that library users will make use of Spring too, but I can distribute Spring with the library.
It's up to your library to instantiate spring the way you need it. This is typically done in your interface entrypoint which delegates to a routine using e.g., ClassPathXmlApplicationContext to configure spring. A sample could be
public class SpringContextLoader {
private static ApplicationContext ctx = null;
public static void init() {
if (ctx == null) {
ctx = ClassPathXmlApplicationContext("classpath:/applicatonContext.xml");
}
}
}
How do I manage my filesystem monitoring thread? Is it good design to expect the program to instantiate a main class of the library and the call init or something like that?
In this case you will probably provide a non-daemon thread, e.g., a thread which must be terminated manually for the application to exit cleanly. Hence you should provide start and stop mechanisms. In your case these probably better be called registerEventListener and unregisterAllEventListener (since I'm guessing you want to pass filesystem events to the client ... ). Another alternative could be to use quartz scheduling with spring.
How do I initialise the library's context? I cannot assume that library users will make use of Spring too, but I can distribute Spring with the library.
You can use a PropertyPlaceholderConfigurer to read configuration settings from a (possibly external) property file, which can be edited by the users. This way users aren't exposed to the details of Spring, not even to XML config files.
How do I manage my filesystem monitoring thread? Is it good design to expect the program to instantiate a main class of the library and the call init or something like that?
I recommend using the Java concurrency framework, specifically a ScheduledExecutorService to run monitoring task(s) at specified intervals. However, you may want to hide this implementation detail from the users of your library, by only exposing some init method to pass in the desired timer interval.
As far as I know, it is not possible to configure your library to start a thread automatically, you have to define a class as starting point. Using Maven you can create an executable jar:
http://maven.apache.org/plugins/maven-shade-plugin/examples/executable-jar.html
In your main class, simple use:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:myspring-context.xml");
context.registerShutdownHook();
For threads, you can try either implementing the runnable interface, and initialize a bean which starts the threads using spring task executors. A more elegant solution that I can suggest is however creating your thread as a pojo and then using spring task scheduler, as follows:
<bean id="threadPojo" class="com.mydomain.ThreadPojo">
</bean>
<task:scheduled-tasks scheduler="mydomainTaskScheduler">
<task:scheduled ref="threadPojo" method="process" fixed-delay="${delay-pool}"/>
</task:scheduled-tasks>
<task:scheduler id="mydomainTaskScheduler" pool-size="${my-pool-size}" />
I hope it will be helpful.

Categories

Resources