Eureka Client and Spring ORM - spring-boot

i have a situation that i need to communicate using eureka client with external api just after spring boot web application up and before JPA start create database schema and insert data into it from the sql file.
the problem is eureka client start registering to eureka server at smartlifecycle phase 0 , which means after application context has been started and JPA already worked and finish its job.
so how to prevent jpa to start working or delay its work to phase 1 for example ?

I was facing the same problem, then I found this SO question.
Basically, you just need to put your communication logic into start().
public class EurekaClientService implements SmartLifecycle {
#Autowired
private EurekaClient eurekaClient;
private volatile boolean isRunning = false;
#Override
public boolean isAutoStartup() {
return true;
}
#Override
public void stop(Runnable r) {
isRunning = false;
}
#Override
public void start() {
eurekaClient.customService();
isRunning = true;
}
#Override
public void stop() {
isRunning = false;
}
#Override
public boolean isRunning() {
return isRunning;
}
#Override
public int getPhase() {
return 1;
}
}
Hope this help

Related

Spring Boot and Spring Data with Cassandra: Continue on failed database connection

I use Spring Boot and Spring Data with Cassandra. On application startup spring establishes a connection to the database to setup the schema and initialize spring data repositories. If the database is not available, the application won't start.
I want, that the application just logs an error and starts. Of course, I can't use the repositories anymore, but other services (rest controllers etc), which are independent from the database should work. It would also be nice to see in actuator healthcheck, that cassandra is down.
For JDBC, there is a spring.datasource.continue-on-error property. I couldn't find something similar for Cassandra.
I also tried to create a custom cassandra configuration and trying to catch Exception on CqlSession creation, but I couldn't achieve the desired behavior.
EDIT: As suggested by #adutra, I tried to set advanced.reconnect-on-init, Application tries to establish the connection, but the application is not fully initialized (e.g. REST controller are not reachable)
#Configuration
public class CustomCassandraConfiguration extends CassandraAutoConfiguration {
#Bean
public DriverConfigLoaderBuilderCustomizer driverConfigLoaderBuilderCustomizer() {
return builder -> builder.withBoolean(DefaultDriverOption.RECONNECT_ON_INIT, true);
}
}
EDIT2: I have now working example (application starts, custom health check for cassandra), but if feels pretty ugly:
CustomCassandraAutoConfiguration
#Configuration
public class CustomCassandraAutoConfiguration extends CassandraAutoConfiguration {
private final Logger logger = LoggerFactory.getLogger(getClass());
#Override
#Bean
public CqlSession cassandraSession(CqlSessionBuilder cqlSessionBuilder) {
try {
return super.cassandraSession(cqlSessionBuilder);
} catch (AllNodesFailedException e) {
logger.error("Failed to establish the database connection", e);
}
return new DatabaseNotConnectedFakeCqlSession();
}
#Bean
public CassandraReactiveHealthIndicator cassandraHealthIndicator(ReactiveCassandraOperations r, CqlSession session) {
if (session instanceof DatabaseNotConnectedFakeCqlSession) {
return new CassandraReactiveHealthIndicator(r) {
#Override
protected Mono<Health> doHealthCheck(Health.Builder builder) {
return Mono.just(builder.down().withDetail("connection", "was not available on startup").build());
}
};
}
return new CassandraReactiveHealthIndicator(r);
}
}
CustomCassandraDataAutoConfiguration
#Configuration
public class CustomCassandraDataAutoConfiguration extends CassandraDataAutoConfiguration {
public CustomCassandraDataAutoConfiguration(CqlSession session) {
super(session);
}
#Bean
public SessionFactoryFactoryBean cassandraSessionFactory(CqlSession session, Environment environment, CassandraConverter converter) {
SessionFactoryFactoryBean sessionFactoryFactoryBean = super.cassandraSessionFactory(environment, converter);
// Disable schema action if database is not available
if (session instanceof DatabaseNotConnectedFakeCqlSession) {
sessionFactoryFactoryBean.setSchemaAction(SchemaAction.NONE);
}
return sessionFactoryFactoryBean;
}
}
DatabaseNotConnectedFakeCqlSession (Fake session implementation)
public class DatabaseNotConnectedFakeCqlSession implements CqlSession {
#Override
public String getName() {
return null;
}
#Override
public Metadata getMetadata() {
return null;
}
#Override
public boolean isSchemaMetadataEnabled() {
return false;
}
#Override
public CompletionStage<Metadata> setSchemaMetadataEnabled( Boolean newValue) {
return null;
}
#Override
public CompletionStage<Metadata> refreshSchemaAsync() {
return null;
}
#Override
public CompletionStage<Boolean> checkSchemaAgreementAsync() {
return null;
}
#Override
public DriverContext getContext() {
return new DefaultDriverContext(new DefaultDriverConfigLoader(), ProgrammaticArguments.builder().build());
}
#Override
public Optional<CqlIdentifier> getKeyspace() {
return Optional.empty();
}
#Override
public Optional<Metrics> getMetrics() {
return Optional.empty();
}
#Override
public <RequestT extends Request, ResultT> ResultT execute( RequestT request, GenericType<ResultT> resultType) {
return null;
}
#Override
public CompletionStage<Void> closeFuture() {
return null;
}
#Override
public CompletionStage<Void> closeAsync() {
return null;
}
#Override
public CompletionStage<Void> forceCloseAsync() {
return null;
}
#Override
public Metadata refreshSchema() {
return null;
}
}
Any suggestions?
You can set the option datastax-java-driver.advanced.reconnect-on-init to true to achieve the effect you want. Its usage is explained in the configuration reference page in the driver docs:
Whether to schedule reconnection attempts if all contact points are unreachable on the first initialization attempt.
If this is true, the driver will retry according to the reconnection policy. The SessionBuilder.build() call - or the future returned by SessionBuilder.buildAsync() - won't complete until a contact point has been reached. If this is false and no contact points are available, the driver will fail with an AllNodesFailedException.
However be careful: with this option set to true, as stated above, any component trying to access a CqlSession bean, even if the session bean is lazy, will block until the driver is able to connect, and might block forever if the contact points are wrong.
If that's not acceptable for you, I would suggest that you wrap the CqlSession bean in another bean that will check if the future returned by SessionBuilder.buildAsync() is done or not, and either block, throw or return null, depending on the caller's expectations.
[EDIT] I've reached out internally to the DataStax Drivers team last night and adutra has responded so I'm withdrawing my response.

How to stop camel context gracefully in Springboot application

I am using Camel with Spring Boot. The camel context is started on application started and it keeps running. On application shut down how to shut down the camel context.
Thanks in advance.
I have written a custom solution by implementing spring's SmartLifeCycle which waits for other spring beans to stop before shutting down CamelContext. Use this class as it is, it would work fine.
#Component
public class SpringBootCamelShutDown implements SmartLifecycle {
private static final Logger log = LoggerFactory.getLogger(SpringBootCamelShutDown.class);
#Autowired
private ApplicationContext appContext;
#Override
public void start() {}
#Override
public void stop() {}
#Override
public boolean isRunning() {
SpringCamelContext context = (SpringCamelContext)appContext.getBean(CamelContext.class);
return context.isStarted();
}
#Override
public boolean isAutoStartup() {
return true;
}
#Override
public void stop(Runnable runnable) {
SpringCamelContext context = (SpringCamelContext)appContext.getBean(CamelContext.class);
if (!isRunning()) {
log.info("Camel context already stopped");
return;
}
log.info("Stopping camel context. Will wait until it is actually stopped");
try {
context.stop();
} catch (Exception e) {
log.error("Error shutting down camel context",e) ;
return;
}
while(isRunning()) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
log.error("Error shutting down camel context",e) ;
}
};
// Calling this method is necessary to make sure spring has been notified on successful
// completion of stop method by reducing the latch countdown value.
runnable.run();
}
#Override
public int getPhase() {
return Integer.MAX_VALUE;
}
}
You can use CamelContext class stop method.
#Autowired CamelContext camelContext;
stop() - to shutdown (will stop all routes/components/endpoints etc and clear internal state/cache)
Refer http://camel.apache.org/spring-boot.html and http://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/CamelContext.html

Spring boot graceful shutdown mid-transaction

I'm working on a spring-boot service that performs sensitive payment processing, and would like to ensure that any shutdown to the app will be done without interrupting these transactions. Curious on how to best approach this in spring-boot.
I read about adding shutdown hooks to spring-boot, and I was thinking maybe to use a CountDownLatch on the class to check if the thread has completed processing - something like this:
#Service
public class PaymentService {
private CountDownLatch countDownLatch;
private void resetLatch() {
this.countDownLatch = new CountDownLatch(1);
}
public void processPayment() {
this.resetLatch();
// do multi-step processing
this.CountDownLatch.countDown();
}
public void shutdown() {
// blocks until latch is available
this.countDownLatch.await();
}
}
// ---
#SpringBootApplication
public class Application {
public static void main(String[] args) {
// init app and get context
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
// retrieve bean needing special shutdown care
PaymentService paymentService = context.getBean(PaymentService.class);
Runtime.getRuntime().addShutdownHook(new Thread(paymentService::shutdown));
}
}
Constructive feedback is greatly appreciated - thanks.
I ended up using #PreDestroy annotation on the shutdown method:
#Service
public class PaymentService {
private CountDownLatch countDownLatch;
private synchronized void beginTransaction() {
this.countDownLatch = new CountDownLatch(1);
}
private synchronized void endTransaction() {
this.countDownLatch.countDown();
}
public void processPayment() {
try {
this.beginTransaction();
// - - - -
// do multi-step processing
// - - - -
} finally {
this.endTransaction();
}
}
#PreDestroy
public void shutdown() {
// blocks until latch is available
this.countDownLatch.await();
}
}

Netty-Socketio with Spring MVC

I am trying to add sockets to my Spring MVC project. I think I should wright something in servlet-context.xml but don`t know what. So I create classes
#Component
public class Bootstrap {
#Autowired
private SocketIOServer server;
#PostConstruct
public void start() {
server.start();
}
#PreDestroy
public void stop() {
server.stop();
}
}
and
#Configuration
#ComponentScan("controllers")
public class SpringConfig {
#Bean(name="webSocketServer")
public SocketIOServer webSocketServer() {
com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration();
config.setHostname("localhost");
config.setPort(4443);
final SocketIOServer server = new SocketIOServer(config);
server.addJsonObjectListener(LogFile.class, new DataListener<LogFile>() {
#Override
public void onData(SocketIOClient client, LogFile data, AckRequest ackSender) {
server.getBroadcastOperations().sendJsonObject(data);
}
});
server.addConnectListener(new ConnectListener() {
#Override
public void onConnect(SocketIOClient client) {
LogFile log = new LogFile();
log.setMsg("hello");
server.getBroadcastOperations().sendJsonObject(log);
}
});
return server;
}
}
But nothing gonna work. I added client to resources of project and enter link localhost there and have client on my local machine with right link. But still can`t connect. What I am doing wrong? I believe that I mistaken in configuring beans.
Samples I get from this https://github.com/mrniko/netty-socketio.

Should the Spring Boot shutdown endpoint shut down the entire JVM process, or just the application context?

I am getting a 200 response from Spring Boot's shutdown endpoint, and I am seeing that the application context shuts down as expected, but then the JVM process itself remains alive forever. Is this the expected behavior of the shutdown endpoint, or is it expected that the process itself would also terminate gracefully?
In http://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-endpoints.html, it says that the shutdown endpoint "allows the application to be gracefully shutdown (not enabled by default)".
Thanks Stéphane, I found what was preventing the JVM process from terminating after hitting the /shutdown endpoint. There was a ScheduledExecutor in one of my dependencies that was not being shut down with the application context, and it was preventing the JVM process from shutting down (even after the application context was closed). I wrote a simple example to show how to reproduce the behavior, and another example showing how to resolve it.
This example will NOT terminate the JVM process when you hit /shutdown endpoint:
#SpringBootApplication
public class AppSpringConfiguration {
public static void main(String[] args) {
SpringApplication.run(AppSpringConfiguration.class);
}
#Bean
public ClassWithExecutor ce() {
return new ClassWithExecutor();
}
#PostConstruct
public void startScheduledTask() {
ce().startScheduledTask();
}
#RestController
public static class BusinessLogicController {
#RequestMapping(value = "/hi")
public String businessLogic() {
return "hi";
}
}
public static class ClassWithExecutor {
ScheduledExecutorService es;
ClassWithExecutor() {
this.es = Executors.newSingleThreadScheduledExecutor();
}
public void startScheduledTask() {
es.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
System.out.println("Printing this every minute");
}
}, 0, 3, TimeUnit.SECONDS);
}
}
}
By adding a shutdown hook that shuts down the ScheduledExecutor when the application context is closing, the JVM process now gets terminated after hitting the /shutdown endpoint:
#SpringBootApplication
public class AppSpringConfiguration {
public static void main(String[] args) {
SpringApplication.run(AppSpringConfiguration.class);
}
#Bean
public ClassWithExecutor ce() {
return new ClassWithExecutor();
}
#Bean
ShutdownAction sa() {
return new ShutdownAction(ce());
}
#PostConstruct
public void startScheduledTask() {
ce().startScheduledTask();
}
#RestController
public static class BusinessLogicController {
#RequestMapping(value = "/hi")
public String businessLogic() {
return "hi";
}
}
public static class ShutdownAction implements ApplicationListener<ContextClosedEvent> {
private ClassWithExecutor classWithExecutor;
ShutdownAction(ClassWithExecutor classWithExecutor) {
this.classWithExecutor = classWithExecutor;
}
#Override
public void onApplicationEvent(ContextClosedEvent event) {
classWithExecutor.shutdown();
}
}
public static class ClassWithExecutor {
ScheduledExecutorService es;
ClassWithExecutor() {
this.es = Executors.newSingleThreadScheduledExecutor();
}
public void startScheduledTask() {
es.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
System.out.println("Printing this every minute");
}
}, 0, 3, TimeUnit.SECONDS);
}
public void shutdown() {
es.shutdownNow();
}
}
}
You have something that prevents the JVM to exit besides your Spring Boot application. If you don't and you have a sample projet that demonstrates the problem, then please create an issue and we'll have a look.
Instead of using the shutdown endpoint, you can use the spring-boot-maven-plugin as of 1.3 that has a start and stop goals to be used in typical integration tests scenarios.
If you have a scheduled executor running you should specify destroy method:
#Bean(destroyMethod = "shutdown")

Resources