Spring boot check external service status on boot - spring

I want check some external http service before my Spring Boot is ready.
The url to the external web service are stored in a property file with a #ConfigurationProperties class.
How do this check i tried using a springApplication.addListner() with a ping method. But the property class have not then been initialized.
public class ApplicationStartListener implements ApplicationListener<ApplicationPreparedEvent> {
#Override
public void onApplicationEvent(ApplicationPreparedEvent event) {
String url = AppProp.getURL();
inet = InetAddress.getByName(url );
inet.isReachable(5000)
...
application.yml
tops:
http://service.com
#Component
#ConfigurationProperties("tops")
public class AppProp{
private static String url;
public static String getUrl() {

The easiest way to accomplish this is to implement the ApplicationRunner interface.
From the Spring Boot documentation [1]
If you need to run some specific code once the SpringApplication has started, you can implement the ApplicationRunner or CommandLineRunner interfaces. Both interfaces work in the same way and offer a single run method which will be called just before SpringApplication.run(…​) completes.
[1] https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-spring-application.html#boot-features-command-line-runner
Assuming you have url defined in application.properties:
#SpringBootApplication
public class MyApplication implements ApplicationRunner
{
#Inject
private AppConfig appConfig;
#Inject
private ConfigurableApplicationContext applicationContext;
public static void main(String[] args)
{
SpringApplication.run(MyApplication.class, args);
}
#Override
public void run(ApplicationArguments args) throws Exception
{
InetAddress inetAddress = InetAddress.getByName(appConfig.getUrl());
if (!inetAddress.isReachable(5000))
{
// Stop the application or do other things
}
}
#Component
#ConfigurationProperties
public static class AppConfig
{
private String url;
public String getUrl()
{
return url;
}
public void setUrl(String url)
{
this.url = url;
}
}
}
If you need even more control than this, you can use SpringApplicationRunListener [2]
[2] http://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/SpringApplicationRunListener.html
#SpringBootApplication
public class MyApplication implements SpringApplicationRunListener
{
public MyApplication() { }
public MyApplication(SpringApplication springApplication, String[] args) { }
public static void main(String[] args)
{
SpringApplication.run(MyApplication.class, args);
}
#Override
public void started() { }
#Override
public void environmentPrepared(ConfigurableEnvironment environment)
{
// 1st opportunity
InetAddress inetAddress = InetAddress.getByName(environment.getProperty("url"));
if (!inetAddress.isReachable(5000))
{
// Stop the application or do other things
}
}
#Override
public void contextPrepared(ConfigurableApplicationContext context)
{
// 2nd opportunity
InetAddress inetAddress = InetAddress.getByName(context.getEnvironment().getProperty("url"));
if (!inetAddress.isReachable(5000))
{
// Stop the application or do other things
}
}
#Override
public void contextLoaded(ConfigurableApplicationContext context)
{
// 3rd opportunity
InetAddress inetAddress = InetAddress.getByName(context.getEnvironment().getProperty("url"));
if (!inetAddress.isReachable(5000))
{
// Stop the application or do other things
}
}
#Override
public void finished(ConfigurableApplicationContext context, Throwable exception)
{
// 4th opportunity
InetAddress inetAddress = InetAddress.getByName(context.getEnvironment().getProperty("url"));
if (!inetAddress.isReachable(5000))
{
// Stop the application or do other things
}
}
#Component
#ConfigurationProperties
public static class AppConfig {
private String url;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
}
then create META-INF\spring.factories and add
org.springframework.boot.SpringApplicationRunListener=com.foobar.MyApplication

Related

Spring JUnit 5 ExtendWith TestContainer

I'm trying to reutilize a container and I'm trying to use JUnit5 ExtendWith feature but I'm still getting:
Connection to localhost:5432 refused.
If I have the same logic inside each test everything works as expected.
#Testcontainers
#SpringBootTest
#ExtendWith({PostgresTestContainersExtension.class})
public class ApplicationJUnit5Test {
#Autowired
private HeroClassicJDBCRepository repositoryUnderTest;
#Test
public void test1() {
System.out.println("junit version: " + Version.id());
Collection<Hero> heroes = repositoryUnderTest.allHeros();
assertThat(heroes).hasSize(1);
repositoryUnderTest.addHero(new Hero("bb", "bb"));
Collection<Hero> heroesAfter = repositoryUnderTest.allHeros();
assertThat(heroesAfter).hasSize(2);
}
}
Extention:
public class PostgresTestContainersExtension implements BeforeAllCallback,
BeforeTestExecutionCallback {
private static final String IMAGE_NAME = "registry.mycomp.com/db/mariadb:10.4.11";
#DynamicPropertySource
static void properties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", container::getJdbcUrl);
registry.add("spring.datasource.password", container::getPassword);
registry.add("spring.datasource.username", container::getUsername);
}
#Container
public static PostgreSQLContainer container = new PostgreSQLContainer()
.withUsername("duke")
.withPassword("password")
.withDatabaseName("test");
#Override
public void beforeAll(ExtensionContext extensionContext) {
startContainerIfNeed();
}
#Override
public void beforeTestExecution(ExtensionContext extensionContext) {
startContainerIfNeed();
}
public void startContainerIfNeed() {
if (!container.isRunning()) {
container.start();
}
}
}
As far as I know #DynamicPropertySource can only be used in the test class itself or a superclass. You’ll have to move the properties method over.

How to use Spring boot AutoWired and ScheduledExecutorService?

I need to use autowired in more than one class with ScheduledExecutorService, what I have tried is shown in this code. logging size of User list in below example always shows 0, even after user added to arraylist. How to properly use Autowired and ScheduledExecutorService in spring boot?
#Component
public class AnotherClass {
List<User> users = new ArrayList();
public void addUser(User user){
users.add(user);
}
public void logUsers(){
logger.info("User size " + users.size()); <================= Always logs 0, when called from executor
}
}
#RestController
public class SecondClass {
#Autowired
private AnotherClass anotherClass;
#GetMapping(value="/user/test")
public void logUsers(){
anotherClass.addUser(new User());
}
}
Application Class
#Component
#SpringBootApplication
public class SpringBootDemoApplication {
private ScheduledExecutorService exec = Executors.newScheduledThreadPool(1);
#Autowired
private AnotherClass anotherClass;
#PostConstruct
public void init() {
logger();
}
public static void main(String[] args) {
SpringApplication.run(SpringBootDemoApplication.class, args);
}
public void logger(){
exec.scheduleAtFixedRate(new Runnable(){
#Override
public void run(){
try {
anotherClass.logUsers();
}catch (Exception e){
}
}
}, 2000, 1000, TimeUnit.MILLISECONDS);
}
}
The code works if you use the Spring #Autowired and not the #AutoWired Annotation.

Why does CachePut not work in this example?

I am playing around with the Spring framework and I would like to get my name returned from the cache. After 5 seconds I will update the cache and I hope to receive a new name.... unfortunately this is not working.... why?
#Component
public class Test {
public String name = "peter";
#Cacheable(value = "numCache")
public String getName() {
return name;
}
#Scheduled(fixedRate = 5000)
#CachePut(value = "numCache")
public String setName() {
this.name = "piet";
return name;
}
}
#Component
public class AppRunner implements CommandLineRunner {
public void run(String... args) throws Exception {
Test test = new Test();
while(true) {
Thread.sleep(1000);
System.out.println(test.getName());
}
}
}
#SpringBootApplication
#EnableCaching
#EnableScheduling
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
You are creating an instance of Test yourself with new, you are not autowiring it. I would try like this:
#Component
public class Test {
public String name = "peter";
#Cacheable(value = "numCache")
public String getName() {
return name;
}
#Scheduled(fixedRate = 5000)
#CachePut(value = "numCache")
public String setName() {
this.name = "piet";
return name;
}
}
#Component
public class AppRunner implements CommandLineRunner {
#Autowired private Test test;
public void run(String... args) throws Exception {
while(true) {
Thread.sleep(1000);
System.out.println(test.getName());
}
}
}

Spring Boot and Java Fx

To start my application, I'm avoiding the "implements CommandLineRunner" process to do the setup but I am facing a problem in this line
fxmlLoader.setControllerFactory(springContext::getBean);
where fxmlLoader is an instance of FxmlLoader and springContext ia an instance of ConfigurableApplicationContext. I am facing this error,
"The method setControllerFactory(Callback<Class<?>,Object>) in the
type FXMLLoader is not applicable for the arguments
(springContext::getBean)".
Can anyone help me with the exact syntax? My imported package reports an error as
"The type org.springframework.beans.factory.annotation.Autowire
cannot be resolved. It is indirectly referenced from required .class
files" .
ok, ive understood that this would require a little code, but ive found already built project with a solution similar to what ive proposed - heres example https://github.com/ruslanys/sample-spring-boot-javafx
you just to tie javafx to spring with context.getAutowireCapableBeanFactory().autowireBean(this);
in AbstractJavaFxApplicationSupport.java file
code will look like this
public abstract class AbstractJavaFxApplicationSupport extends Application {
private static String[] savedArgs;
protected ConfigurableApplicationContext context;
#Override
public void init() throws Exception {
context = SpringApplication.run(getClass(), savedArgs);
context.getAutowireCapableBeanFactory().autowireBean(this);
}
#Override
public void stop() throws Exception {
super.stop();
context.close();
}
protected static void launchApp(Class<? extends AbstractJavaFxApplicationSupport> appClass, String[] args) {
AbstractJavaFxApplicationSupport.savedArgs = args;
Application.launch(appClass, args);
}
}
and tie all you view like bean
#Configuration
public class ControllersConfiguration {
#Bean(name = "mainView")
public ViewHolder getMainView() throws IOException {
return loadView("fxml/main.fxml");
}
#Bean
public MainController getMainController() throws IOException {
return (MainController) getMainView().getController();
}
protected ViewHolder loadView(String url) throws IOException {
InputStream fxmlStream = null;
try {
fxmlStream = getClass().getClassLoader().getResourceAsStream(url);
FXMLLoader loader = new FXMLLoader();
loader.load(fxmlStream);
return new ViewHolder(loader.getRoot(), loader.getController());
} finally {
if (fxmlStream != null) {
fxmlStream.close();
}
}
}
public class ViewHolder {
private Parent view;
private Object controller;
public ViewHolder(Parent view, Object controller) {
this.view = view;
this.controller = controller;
}
public Parent getView() {
return view;
}
public void setView(Parent view) {
this.view = view;
}
public Object getController() {
return controller;
}
public void setController(Object controller) {
this.controller = controller;
}
}
}
then in controller you may enjoy spring magic and javafx magic together
public class MainController {
#Autowired private ContactService contactService;
#FXML private TableView<Contact> table;
#FXML private TextField txtName;
#FXML private TextField txtPhone;
#FXML private TextField txtEmail;}
and just start your app like this
#SpringBootApplication
public class Application extends AbstractJavaFxApplicationSupport {
#Value("${ui.title:JavaFX приложение}")//
private String windowTitle;
#Qualifier("mainView")
#Autowired
private ControllersConfiguration.ViewHolder view;
#Override
public void start(Stage stage) throws Exception {
stage.setTitle(windowTitle);
stage.setScene(new Scene(view.getView()));
stage.setResizable(true);
stage.centerOnScreen();
stage.show();
}
public static void main(String[] args) {
launchApp(Application.class, args);
}}

Expose Togglz Admin console in Spring Boot on management-port

By default Togglz admin console runs on application port (configured by server.port property). I want to expose it on management.port. My question: is it possible?
If you use Togglz >= 2.4.0 then this feature is available out of the box.
For older releases solution is below:
I managed to expose a raw servlet on management.port by wrapping it with MvcEndpoint.
The easiest way to do it to use Spring Cloud module which does all the job for you (for example in the HystrixStreamEndpoint):
public class HystrixStreamEndpoint extends ServletWrappingEndpoint {
public HystrixStreamEndpoint() {
super(HystrixMetricsStreamServlet.class, "hystrixStream", "/hystrix.stream",
true, true);
}
}
In the case of TogglzConsoleServlet there is unfortunately one more hack to do with path's due to the way it extracts prefix from request URI, so the whole solution looks a little bit ugly:
#Component
class TogglzConsoleEndpoint implements MvcEndpoint {
private static final String ADMIN_CONSOLE_URL = "/togglz-console";
private final TogglzConsoleServlet togglzConsoleServlet;
#Autowired
TogglzConsoleEndpoint(final ServletContext servletContext) throws ServletException {
this.togglzConsoleServlet = new TogglzConsoleServlet();
togglzConsoleServlet.init(new DelegatingServletConfig(servletContext));
}
#Override
public String getPath() {
return ADMIN_CONSOLE_URL;
}
#Override
public boolean isSensitive() {
return true;
}
#Override
public Class<? extends Endpoint> getEndpointType() {
return null;
}
#RequestMapping("**")
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequestWrapper requestWrapper = new HttpServletRequestWrapper(request) {
#Override
public String getServletPath() {
return ADMIN_CONSOLE_URL;
}
};
togglzConsoleServlet.service(requestWrapper, response);
return null;
}
private class DelegatingServletConfig implements ServletConfig {
private final ServletContext servletContext;
DelegatingServletConfig(final ServletContext servletContext) {
this.servletContext = servletContext;
}
#Override
public String getServletName() {
return TogglzConsoleEndpoint.this.togglzConsoleServlet.getServletName();
}
#Override
public ServletContext getServletContext() {
return servletContext;
}
#Override
public String getInitParameter(final String name) {
return servletContext.getInitParameter(name);
}
#Override
public Enumeration<String> getInitParameterNames() {
return servletContext.getInitParameterNames();
}
}
}

Resources