We're building an app using Grails 2.0.4, GORM, and Hibernate. When the database is not available, Grails will not initialize, and startup fails. We thought our pool settings would protect against startup failures, but that doesn't seem to be the case.
If pool settings alone can't address this, is it possible to catch exceptions in resources.groovy where, if a database service can't be initialized, switch to a file-based service temporarily? Something like this...
resources.groovy
try{
myDataService(PostgresDatabaseServiceImpl){}
}catch(Exception e){
//if database connect failed, use local service instead
myDataService(FileBasedServiceImpl){}
}
Even if the above is possible, it creates a new problem; how to switch back, dynamically, once the database is available. We attempted the above try/catch, but it had no impact, the startup issue persists:
Error creating bean with name 'transactionManagerPostProcessor':
Initialization of bean failed
If it's possible to avoid startup failures through pool settings alone, we could certainly manage SQL exceptions at runtime when the app attempts to use bad database connections, but startup failures we can't manage.
DataSource.groovy (pool settings)
dataSource {
pooled = true
driverClassName = "org.postgresql.Driver"
properties {
maxActive = 20
minEvictableIdleTimeMillis=1800000
timeBetweenEvictionRunsMillis=1800000
numTestsPerEvictionRun=3
testOnBorrow=true
testWhileIdle=true
testOnReturn=true
validationQuery="SELECT 1"
}
}
hibernate {
cache.use_second_level_cache = false
cache.use_query_cache = false
cache.region.factory_class = 'net.sf.ehcache.hibernate.EhCacheRegionFactory'
}
We attempted the above try/catch, but it had no impact, the startup issue persists:
So it seems you already have the answer to the question of whether it's possible to register a Spring bean for a (potentially) unavailable database in resources.groovy.
As an alternative, you could try registering a Spring bean for the database at runtime. This advantage of this approach is that even if registering the bean fails, you will be able to catch the error and use the file-based service instead. An example of how to register DataSource beans at runtime is show here.
To use this approach, register only a bean for the file-based service in resources.groovy
myDataService(FileBasedServiceImpl)
Then when you need to access the datasource:
class DataSourceService implements ApplicationContextAware {
def myDataService
ApplicationContext applicationContext
private static PG_BEAN = 'postgres'
def getDataSource() {
try {
getPostgresService()
} catch (ex) {
myDataService
}
}
private getPostgresService() {
def postgres
if (applicationContext.containsBean(PG_BEAN)) {
postgres = applicationContext.getBean(PG_BEAN)
} else {
// register a bean under the name 'postGres' and store a reference to it in postgres
// https://stackoverflow.com/a/20634968/2648
}
checkPostgres(postgres)
}
private checkPostres(postgresBean) {
// check that the database is available, throw an exception if it's not, return
// postgresBean if it is
}
}
Related
I want to create a microservice with Spring Boot. For persistence i use a mariadb database. To wait for the database which is running in a docker container, i implemented the following code like shown here:
#Bean
public DatabaseStartupValidator databaseStartupValidator(DataSource dataSource) {
var dsv = new DatabaseStartupValidator();
dsv.setDataSource(dataSource);
dsv.setTimeout(60);
dsv.setInterval(7);
dsv.setValidationQuery(DatabaseDriver.MYSQL.getValidationQuery());
return dsv;
}
The code is working very well, my application is now waiting for the database connection. But i get an exception at startup of the application:
java.sql.SQLNonTransientConnectionException: Could not connect to Host ....
...
...
...
In the next line i get an information, that it will wait for the database:
021-04-07 21:29:40.816 INFO 16569 --- [ main] o.s.j.support.DatabaseStartupValidator : Database has not started up yet - retrying in 7 seconds (timeout in 57.65 seconds)
After that the application is starting as expected. So i think everything is working fine, but what i have to do to suppress the Exception? In the linked article it should work without an exception. Do i have to implement the "dependsOnPostProcessor" function? Which dependency i have to use? Sorry, possible a dumb question, i am new to spring boot.
to get rid of that exception you can state the below directive in your application.properties file:
logging.level.com.zaxxer.hikari=OFF
Keep in mind that if the application will not be able to get in contact with the db your spring crashes after a while due to that exception. In addition the above directive prevent you to see any logging activity related to Hikari.
In summary you hide the appearance of the exception until it is possible before the application dies due to timeout.
hoping I clarified a bit the case
Yes indeed you need to add the "depends-on" for the beans that rely on the data source. Note the following part of the documentation:
To be referenced via "depends-on" from beans that depend on database startup, like a Hibernate SessionFactory or custom data access objects that access a DataSource directly.
If I understand it well, this means that beans such as an EntityManagerFactory which rely on the database will now have to go through the DatabaseStartupValidator bean and wait for the DB startup. I don't know what caused your exception, but usually there is an EntityManagerFactory involved, so try adding the DependsOn on this object at least.
This is how the linked article is doing it:
#Bean
public static BeanFactoryPostProcessor dependsOnPostProcessor() {
return bf -> {
// Let beans that need the database depend on the DatabaseStartupValidator
// like the JPA EntityManagerFactory or Flyway
String[] flyway = bf.getBeanNamesForType(Flyway.class);
Stream.of(flyway)
.map(bf::getBeanDefinition)
.forEach(it -> it.setDependsOn("databaseStartupValidator"));
String[] jpa = bf.getBeanNamesForType(EntityManagerFactory.class);
Stream.of(jpa)
.map(bf::getBeanDefinition)
.forEach(it -> it.setDependsOn("databaseStartupValidator"));
};
}
You may not necessarily have Flyway configured, but the main thing to note is the dependency itself is referenced by the bean name databaseStartupValidator which is the name of the method that creates the bean.
I'm using Hibernate 5.4.18 with HikariCP 3.4.5. My configuration is programmatic, and I set underlying DataSource of Hibernate with hibernate.connection.datasource-property. Strangely, when I then call EntityManagerFactory.close()-function, it doesn't call close()-method of HikariDataSource, and connection will leave open. Is this a desired behavior? Oracle documentation says that EntityManagerFactory.close() will "Close the factory, releasing any resources that it holds".
Minimum example with Kotlin:
fun main() {
val emf = Persistence.createEntityManagerFactory("default", getJpaProperties())
// Fetch underlying HikariDataSource
val ds = emf.unwrap(SessionFactoryImpl::class.java)
.serviceRegistry
.getService<ConnectionProvider>(ConnectionProvider::class.java)
.unwrap(HikariDataSource::class.java)
emf.close()
println(ds.isClosed) // prints "false"
}
private fun getJpaProperties(): Map<String, Any> {
val dataSource = HikariDataSource().apply {
username = "sa"
password = ""
jdbcUrl = "jdbc:h2:mem:test_db"
}
return mapOf(
"hibernate.dialect" to "org.hibernate.dialect.H2Dialect",
"hibernate.connection.datasource" to dataSource
)
}
It's because you are providing an instance of a datasource. If you initialise a DS, there's a big chance you'll use it in other parts of your code, so closing the datasource would introduce an unexpected behaviour. This is actually a good practice, that the "module" that creates a resource is also responsible for disposing of it.
Hibernate will close the datasource if you provide the details of it (username, password, class name, etc), as it will be managed by Hibernate.
For a bit of history, in the old days, a DS would be created in by a J2EE container (e.g. Tomcat) and then shared across many apps inside that container. And the property hibernate.connection.datasource would be a JNDI location pointing to the datasource.
For this requirement we tried to create the datasource using the org.wso2.carbon.user.core.util.DatabaseUtil class by passing the realm, but we always get an exception saying error in looking up datasource.
We understand that during server startup, org.wso2.carbon.user.core.internal.Activator -> startDeploy(BundleContext bundleContext) is invoked and it creates a new RealmService instance where the realmconfiguration and datasource objects are successfully initialized. In the Activator class initialized realmservice instance is set to UserCoreUtil class(UserCoreUtil.setRealmService(realmService)). RealmService initialization invokes the DefaultRealmService, where the datasource instance is initialized and that object is added to the properties.
For any of user or tenant related DB operations below call is invoked, CarbonContext.getThreadLocalCarbonContext().getUserRealm() method is invoked which actually uses the datasource from the properties which was stored by DefaultRealmService during the server start up and it creates the userStoreManager instance and returns the userRealm through which all user related operations are performed.
For accessing the application specific table, we created our own JDBCCustomManager class and tried to perform JDBC operations. We need the datasource to do DB operations for that when we execute, “DatabaseUtil.getRealmDataSource(objRealmService.getBootstrapRealmConfiguration())”, we always get an exception "Error in looking up data source: jdbc/WSO2CarbonDB".
If we write methods to access our table in the JDBCUserStoreManager its working but which is not the proper way to do. Can you please suggest is there any other way to get hold of datasources object of WSO2 so that we can use it in the application.
Your description is not much clear. If your are trying to get an datasource object you can do it like this.
public static DataSource lookupDataSource(String dataSourceName, final Hashtable<Object, Object> jndiProperties) {
try {
if (jndiProperties == null || jndiProperties.isEmpty()) {
return (DataSource) InitialContext.doLookup(dataSourceName);
}
final InitialContext context = new InitialContext(jndiProperties);
return (DataSource) context.doLookup(dataSourceName);
} catch (Exception e) {
throw new RuntimeException("Error in looking up data source: " + e.getMessage(), e);
}
}
You can defind the datasource in master-datasource.xml and give it a JNDI name which is used for the lookup.
I know one of the purposes of Spring is to fail-fast if any bean initialization fails, but I'm curious if the following behavior is possible.
I have an application that depends on many external servers and DBs, and so there are a dozen beans that are interfaces for the different external services. Config values of the form 'db1.enabled=true' and 'db2.enabled=false' define whether these beans should even be initialized. What I want is a 'best effort' scenario where, if db1.enabled is set to true, but the db in question is not reachable for whatever reason and initialization of the bean that calls db1 fails, the whole system shouldn't crash. The effect should be as if db1.enabled was set to false, with ideally an error message somewhere indicating that this occurred. Doing this for all beans would defeat the purpose of Spring, but it would be useful for certain ones.
If you use Java Config you can run basically any code as bean initialization. You can, for example, put something like this in your config:
#Configuration
public class MyConfig{
#Bean
public DataSource mainDataSource(){
try{
// initialize your basic datasource here
} catch(Exception e) {
// try something else here
}
}
}
I have configured MysqlDataSource in tomcat using this link.I have written junit test cases.when am i calling below connection from junit it throws following errors.
javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
I have used following code
class DataConnection {
private static DataSource dataSource;
public DataConnection() {
try {
Context ctx = new InitialContext();
dataSource = (DataSource)ctx.lookup("java:comp/env/jdbc/test");
} catch (NamingException e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
new DataConnection();
Connection con=dataSource.getConnection();
return con;
}
}
How to call tomcat from junit? How to achieve this?
The code you give gets the database connection from JNDI, e.g. when running in tomcat from the container. However, for Unit Tests (assuming that's what you use JUnit for) I'd rather suggest to use "dependency injection" - e.g. explicitly pass a database connection to the code under test or manually set it up before the test runs.
There's no need to rely on JNDI for executing your tests: That's not what you want to test, instead, you want to just verify that your actual code is running correctly.
You don't need any fancy library (e.g. spring) for dependency injection, just a slightly adjusted architecture. This will greatly enhance the testability of your application and lower the execution time of your tests.
(This is based on my assumptions of your situation based on the little bit of information that you give in your question)
Give TomcatJNDI a try. It is based on embedded Tomcat but initializes only Tomcat's JNDI environment without starting a server. So you can access all your resources as configured in Tomcat's configuration files in tests or from within any Java SE application. The API is simple. For instance to get a DataSource declared in context.xml:
TomcatJNDI tomcatJNDI = new TomcatJNDI();
tomcatJNDI.processContextXml(contextXmlFile);
tomcatJNDI.start();
Then you can lookup the DataSource as usual
DataSource ds = (DataSource) InitialContext.doLookup("java:comp/env/path/to/datasource")
More information about TomcatJNDI can be found here.