From c900af9fa3f394d037cb8793a9b248614d5c556d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Thomas?= Date: Tue, 7 Jan 2020 23:09:02 +0100 Subject: [PATCH] Avoid exceptions if database is not ready and fully migrated on first startup In particular, this should fix an issue described in #884 (and reproduced since), where Airsonic tries to access the database while Liquibase has not finished running all the migrations. 2020-01-07 23:00:26.697 ERROR --- o.a.p.service.PodcastService : Failed to initialize PodcastService: org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [select id, url, title, description, image_url, status, error_message from podcast_channel]; nested exception is java.sql.SQLException: Table not found in statement [select id, url, title, description, image_url, status, error_message from podcast_channel] org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [select id, url, title, description, image_url, status, error_message from podcast_channel]; nested exception is java.sql.SQLException: Table not found in statement [select id, url, title, description, image_url, status, error_message from podcast_channel] at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:230) ~[spring-jdbc-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:73) ~[spring-jdbc-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:654) ~[spring-jdbc-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:688) ~[spring-jdbc-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:720) ~[spring-jdbc-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:730) ~[spring-jdbc-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:780) ~[spring-jdbc-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.airsonic.player.dao.AbstractDao.query(AbstractDao.java:93) ~[classes!/:10.6.0-SNAPSHOT] at org.airsonic.player.dao.PodcastDao.getAllChannels(PodcastDao.java:73) ~[classes!/:10.6.0-SNAPSHOT] at org.airsonic.player.dao.PodcastDao$$FastClassBySpringCGLIB$$9fcd2715.invoke() ~[classes!/:10.6.0-SNAPSHOT] at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:736) ~[spring-aop-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136) ~[spring-tx-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:671) ~[spring-aop-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.airsonic.player.dao.PodcastDao$$EnhancerBySpringCGLIB$$b59c1dc5.getAllChannels() ~[classes!/:10.6.0-SNAPSHOT] at org.airsonic.player.service.PodcastService.getAllChannels(PodcastService.java:185) ~[classes!/:10.6.0-SNAPSHOT] at org.airsonic.player.service.PodcastService.init(PodcastService.java:112) ~[classes!/:10.6.0-SNAPSHOT] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na] at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:366) ~[spring-beans-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:311) ~[spring-beans-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:134) ~[spring-beans-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:407) ~[spring-beans-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1622) ~[spring-beans-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553) ~[spring-beans-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:481) ~[spring-beans-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:312) ~[spring-beans-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:308) ~[spring-beans-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:211) ~[spring-beans-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1131) ~[spring-beans-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1059) ~[spring-beans-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:583) ~[spring-beans-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87) ~[spring-beans-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:364) ~[spring-beans-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1268) ~[spring-beans-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:551) ~[spring-beans-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:481) ~[spring-beans-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:312) ~[spring-beans-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:308) ~[spring-beans-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:756) ~[spring-beans-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867) ~[spring-context-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:542) ~[spring-context-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:123) ~[spring-boot-1.5.22.RELEASE.jar!/:1.5.22.RELEASE] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:666) ~[spring-boot-1.5.22.RELEASE.jar!/:1.5.22.RELEASE] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:353) ~[spring-boot-1.5.22.RELEASE.jar!/:1.5.22.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:300) ~[spring-boot-1.5.22.RELEASE.jar!/:1.5.22.RELEASE] at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:135) ~[spring-boot-1.5.22.RELEASE.jar!/:1.5.22.RELEASE] at org.airsonic.player.Application.main(Application.java:207) ~[classes!/:10.6.0-SNAPSHOT] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na] at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48) ~[airsonic.war:10.6.0-SNAPSHOT] at org.springframework.boot.loader.Launcher.launch(Launcher.java:87) ~[airsonic.war:10.6.0-SNAPSHOT] at org.springframework.boot.loader.Launcher.launch(Launcher.java:50) ~[airsonic.war:10.6.0-SNAPSHOT] at org.springframework.boot.loader.WarLauncher.main(WarLauncher.java:59) ~[airsonic.war:10.6.0-SNAPSHOT] Caused by: java.sql.SQLException: Table not found in statement [select id, url, title, description, image_url, status, error_message from podcast_channel] at org.hsqldb.jdbc.Util.throwError(Unknown Source) ~[hsqldb-1.8.0.7.jar!/:private-2006/09/24-10:30:15] at org.hsqldb.jdbc.jdbcPreparedStatement.(Unknown Source) ~[hsqldb-1.8.0.7.jar!/:private-2006/09/24-10:30:15] at org.hsqldb.jdbc.jdbcConnection.prepareStatement(Unknown Source) ~[hsqldb-1.8.0.7.jar!/:private-2006/09/24-10:30:15] at org.springframework.jdbc.core.JdbcTemplate$SimplePreparedStatementCreator.createPreparedStatement(JdbcTemplate.java:1525) ~[spring-jdbc-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:632) ~[spring-jdbc-4.3.25.RELEASE.jar!/:4.3.25.RELEASE] ... 61 common frames omitted --- .../java/org/airsonic/player/spring/DatabaseConfiguration.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/airsonic-main/src/main/java/org/airsonic/player/spring/DatabaseConfiguration.java b/airsonic-main/src/main/java/org/airsonic/player/spring/DatabaseConfiguration.java index 6aa1d455..7c6aa39e 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/spring/DatabaseConfiguration.java +++ b/airsonic-main/src/main/java/org/airsonic/player/spring/DatabaseConfiguration.java @@ -10,6 +10,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.DependsOn; import org.springframework.context.annotation.Profile; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jdbc.datasource.DriverManagerDataSource; @@ -33,12 +34,14 @@ public class DatabaseConfiguration { @Bean @Profile("legacy") + @DependsOn("liquibase") public DaoHelper legacyDaoHelper(DataSource dataSource) { return new LegacyHsqlDaoHelper(dataSource); } @Bean @ConditionalOnMissingBean + @DependsOn("liquibase") public DaoHelper daoHelper(DataSource dataSource) { return new GenericDaoHelper(dataSource); }