From 5b6bc43a15e13713f91355cb30ac879cea4fde5a Mon Sep 17 00:00:00 2001 From: Konstantin Krivopustov Date: Wed, 29 Mar 2017 17:37:26 +0400 Subject: [PATCH] PL-8744 Handling of database connection problems on middleware startup --- .../haulmont/cuba/core/app/ServerInfo.java | 2 +- .../cuba/core/app/scheduling/Scheduling.java | 7 +- .../cuba/core/jmx/PersistenceManager.java | 4 +- .../cuba/core/sys/AppContextLoader.java | 53 ++++++++++++++- .../core/sys/dbupdate/DbUpdaterEngine.java | 14 ++-- .../cuba/core/sys/utils/DbUpdaterUtil.java | 4 +- .../cuba/security/app/LoginWorkerBean.java | 46 ++++--------- .../core/sys/AbstractWebAppContextLoader.java | 14 +++- .../core/sys/CubaThreadPoolTaskScheduler.java | 1 + ...on.java => DbInitializationException.java} | 64 +++++++++++-------- .../com/haulmont/cuba/core/sys/DbUpdater.java | 6 +- 11 files changed, 137 insertions(+), 78 deletions(-) rename modules/global/src/com/haulmont/cuba/core/sys/{DBNotInitializedException.java => DbInitializationException.java} (60%) diff --git a/modules/core/src/com/haulmont/cuba/core/app/ServerInfo.java b/modules/core/src/com/haulmont/cuba/core/app/ServerInfo.java index c1c4731e3d..a52914ba51 100644 --- a/modules/core/src/com/haulmont/cuba/core/app/ServerInfo.java +++ b/modules/core/src/com/haulmont/cuba/core/app/ServerInfo.java @@ -190,7 +190,7 @@ public class ServerInfo implements ServerInfoAPI, AppContext.Listener, Ordered { ); } } catch (Exception e) { - log.error("Unable to update SYS_SERVER: {}", e); + log.error("Unable to update SYS_SERVER: {}", e.toString()); } } diff --git a/modules/core/src/com/haulmont/cuba/core/app/scheduling/Scheduling.java b/modules/core/src/com/haulmont/cuba/core/app/scheduling/Scheduling.java index 800570602f..8339ae1a5c 100644 --- a/modules/core/src/com/haulmont/cuba/core/app/scheduling/Scheduling.java +++ b/modules/core/src/com/haulmont/cuba/core/app/scheduling/Scheduling.java @@ -147,7 +147,12 @@ public class Scheduling implements SchedulingAPI { @Override public boolean isActive() { - return configuration.getConfig(ServerConfig.class).getSchedulingActive(); + try { + return configuration.getConfig(ServerConfig.class).getSchedulingActive(); + } catch (Exception e) { + log.error("Unable to find out if scheduling is active: {}", e.toString()); + return false; + } } @Override diff --git a/modules/core/src/com/haulmont/cuba/core/jmx/PersistenceManager.java b/modules/core/src/com/haulmont/cuba/core/jmx/PersistenceManager.java index b2e62d826e..8044a7d53d 100644 --- a/modules/core/src/com/haulmont/cuba/core/jmx/PersistenceManager.java +++ b/modules/core/src/com/haulmont/cuba/core/jmx/PersistenceManager.java @@ -27,7 +27,7 @@ import com.haulmont.cuba.core.global.Configuration; import com.haulmont.cuba.core.global.Metadata; import com.haulmont.cuba.core.global.QueryParser; import com.haulmont.cuba.core.global.QueryTransformerFactory; -import com.haulmont.cuba.core.sys.DBNotInitializedException; +import com.haulmont.cuba.core.sys.DbInitializationException; import com.haulmont.cuba.core.sys.DbUpdater; import com.haulmont.cuba.core.sys.persistence.DbmsType; import com.haulmont.cuba.security.app.Authenticated; @@ -179,7 +179,7 @@ public class PersistenceManager implements PersistenceManagerMBean { return sb.toString(); } else return "No updates available"; - } catch (DBNotInitializedException e) { + } catch (DbInitializationException e) { return e.getMessage(); } catch (Throwable e) { return ExceptionUtils.getStackTrace(e); diff --git a/modules/core/src/com/haulmont/cuba/core/sys/AppContextLoader.java b/modules/core/src/com/haulmont/cuba/core/sys/AppContextLoader.java index 26ac1e0eeb..fe5b1ec0c3 100644 --- a/modules/core/src/com/haulmont/cuba/core/sys/AppContextLoader.java +++ b/modules/core/src/com/haulmont/cuba/core/sys/AppContextLoader.java @@ -101,9 +101,58 @@ public class AppContextLoader extends AbstractWebAppContextLoader { isMaster = clusterManager.isMaster(); } // Init database + DbUpdater updater = (DbUpdater) AppContext.getApplicationContext().getBean(DbUpdater.NAME); if (isMaster && Boolean.valueOf(AppContext.getProperty("cuba.automaticDatabaseUpdate"))) { - DbUpdater updater = (DbUpdater) AppContext.getApplicationContext().getBean(DbUpdater.NAME); - updater.updateDatabase(); + updateDatabase(updater); + } else { + checkDatabase(updater); + } + } + + protected void updateDatabase(DbUpdater updater) { + while (true) { + try { + updater.updateDatabase(); + return; + } catch (DbInitializationException e) { + if (e.isRetryPossible()) { + log.error("Error updating database: {}\nWaiting 5 sec and retrying...", e.toString()); + try { + Thread.sleep(5000); + } catch (InterruptedException e1) { + Thread.currentThread().interrupt(); + } + } else { + throw new RuntimeException("Error updating database", e); + } + } + } + } + + protected void checkDatabase(DbUpdater updater) { + while (true) { + try { + boolean initialized = updater.dbInitialized(); + if (!initialized) { + throw new IllegalStateException("\n" + + "============================================================================\n" + + "ERROR: Database is not initialized. Set 'cuba.automaticDatabaseUpdate'\n" + + "application property to 'true' to initialize and update database on startup.\n" + + "============================================================================"); + } + return; + } catch (DbInitializationException e) { + if (e.isRetryPossible()) { + log.error("Error connecting to database: {}\nWaiting 5 sec and retrying...", e.toString()); + try { + Thread.sleep(5000); + } catch (InterruptedException e1) { + Thread.currentThread().interrupt(); + } + } else { + throw new RuntimeException("Error checking database", e); + } + } } } } diff --git a/modules/core/src/com/haulmont/cuba/core/sys/dbupdate/DbUpdaterEngine.java b/modules/core/src/com/haulmont/cuba/core/sys/dbupdate/DbUpdaterEngine.java index aed7c91f33..d3b08fe1c5 100644 --- a/modules/core/src/com/haulmont/cuba/core/sys/dbupdate/DbUpdaterEngine.java +++ b/modules/core/src/com/haulmont/cuba/core/sys/dbupdate/DbUpdaterEngine.java @@ -19,7 +19,7 @@ package com.haulmont.cuba.core.sys.dbupdate; import com.haulmont.bali.db.DbUtils; import com.haulmont.bali.db.QueryRunner; -import com.haulmont.cuba.core.sys.DBNotInitializedException; +import com.haulmont.cuba.core.sys.DbInitializationException; import com.haulmont.cuba.core.sys.DbUpdater; import com.haulmont.cuba.core.sys.PostUpdateScripts; import com.haulmont.cuba.core.sys.persistence.DbmsSpecificFactory; @@ -81,7 +81,7 @@ public class DbUpdaterEngine implements DbUpdater { } @Override - public void updateDatabase() { + public void updateDatabase() throws DbInitializationException { if (dbInitialized()) doUpdate(); else @@ -89,11 +89,11 @@ public class DbUpdaterEngine implements DbUpdater { } @Override - public List findUpdateDatabaseScripts() throws DBNotInitializedException { + public List findUpdateDatabaseScripts() throws DbInitializationException { List list = new ArrayList<>(); if (dbInitialized()) { if (!changelogTableExists) { - throw new DBNotInitializedException( + throw new DbInitializationException( "Unable to determine required updates because SYS_DB_CHANGELOG table doesn't exist"); } else { List files = getUpdateScripts(); @@ -106,7 +106,7 @@ public class DbUpdaterEngine implements DbUpdater { } } } else { - throw new DBNotInitializedException( + throw new DbInitializationException( "Unable to determine required updates because SEC_USER table doesn't exist"); } return list; @@ -147,7 +147,7 @@ public class DbUpdaterEngine implements DbUpdater { return path.substring(indexOfDir + dir.length() + 1).replaceAll("^/+", ""); } - protected boolean dbInitialized() { + public boolean dbInitialized() throws DbInitializationException { log.trace("Checking if the database is initialized"); Connection connection = null; try { @@ -169,7 +169,7 @@ public class DbUpdaterEngine implements DbUpdater { } return found; } catch (SQLException e) { - throw new RuntimeException("An error occurred while checking database", e); + throw new DbInitializationException(true, "Error connecting to database: " + e.getMessage(), e); } finally { if (connection != null) try { diff --git a/modules/core/src/com/haulmont/cuba/core/sys/utils/DbUpdaterUtil.java b/modules/core/src/com/haulmont/cuba/core/sys/utils/DbUpdaterUtil.java index 3762e26d8f..7b4377c151 100644 --- a/modules/core/src/com/haulmont/cuba/core/sys/utils/DbUpdaterUtil.java +++ b/modules/core/src/com/haulmont/cuba/core/sys/utils/DbUpdaterUtil.java @@ -19,7 +19,7 @@ package com.haulmont.cuba.core.sys.utils; import com.haulmont.cuba.core.sys.AppComponents; import com.haulmont.cuba.core.sys.AppContext; -import com.haulmont.cuba.core.sys.DBNotInitializedException; +import com.haulmont.cuba.core.sys.DbInitializationException; import com.haulmont.cuba.core.sys.dbupdate.DbUpdaterEngine; import com.haulmont.cuba.core.sys.dbupdate.ScriptResource; import org.apache.commons.cli.*; @@ -230,7 +230,7 @@ public class DbUpdaterUtil extends DbUpdaterEngine { updatesAvailable = true; } else log.info("No available updates found for database"); - } catch (DBNotInitializedException e) { + } catch (DbInitializationException e) { log.warn("Database not initialized"); return; } diff --git a/modules/core/src/com/haulmont/cuba/security/app/LoginWorkerBean.java b/modules/core/src/com/haulmont/cuba/security/app/LoginWorkerBean.java index 2c893c919b..ed4050be76 100644 --- a/modules/core/src/com/haulmont/cuba/security/app/LoginWorkerBean.java +++ b/modules/core/src/com/haulmont/cuba/security/app/LoginWorkerBean.java @@ -34,11 +34,9 @@ import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - -import org.springframework.core.Ordered; import org.springframework.stereotype.Component; + import javax.annotation.Nullable; -import javax.annotation.PostConstruct; import javax.inject.Inject; import java.util.*; @@ -48,7 +46,7 @@ import java.util.*; * @see com.haulmont.cuba.security.app.LoginServiceBean */ @Component(LoginWorker.NAME) -public class LoginWorkerBean implements LoginWorker, AppContext.Listener, Ordered { +public class LoginWorkerBean implements LoginWorker { private final Logger log = LoggerFactory.getLogger(LoginWorkerBean.class); @@ -414,16 +412,17 @@ public class LoginWorkerBean implements LoginWorker, AppContext.Listener, Ordere @Override public UserSession getSession(UUID sessionId) { - try { - //noinspection UnnecessaryLocalVariable - UserSession session = userSessionManager.getSession(sessionId); - return session; - } catch (RuntimeException e) { - if (e instanceof NoUserSessionException) - return null; - else - throw e; + UserSession session = userSessionManager.findSession(sessionId); + if (session == null && sessionId.equals(configuration.getConfig(GlobalConfig.class).getAnonymousSessionId())) { + synchronized (this) { + session = userSessionManager.findSession(sessionId); + if (session == null) { + initializeAnonymousSession(); + session = userSessionManager.findSession(sessionId); + } + } } + return session; } @Override @@ -499,13 +498,8 @@ public class LoginWorkerBean implements LoginWorker, AppContext.Listener, Ordere } } - @PostConstruct - public void init() { - AppContext.addListener(this); - } - protected void initializeAnonymousSession() { - log.debug("Initialize anonymous session"); + log.info("Initializing anonymous session"); try { UserSession session = loginAnonymous(); @@ -515,18 +509,4 @@ public class LoginWorkerBean implements LoginWorker, AppContext.Listener, Ordere log.error("Unable to login anonymous session", e); } } - - @Override - public void applicationStarted() { - initializeAnonymousSession(); - } - - @Override - public void applicationStopped() { - } - - @Override - public int getOrder() { - return LOWEST_PLATFORM_PRECEDENCE - 110; - } } \ No newline at end of file diff --git a/modules/global/src/com/haulmont/cuba/core/sys/AbstractWebAppContextLoader.java b/modules/global/src/com/haulmont/cuba/core/sys/AbstractWebAppContextLoader.java index 3268a94fc7..327edd24b6 100644 --- a/modules/global/src/com/haulmont/cuba/core/sys/AbstractWebAppContextLoader.java +++ b/modules/global/src/com/haulmont/cuba/core/sys/AbstractWebAppContextLoader.java @@ -26,6 +26,8 @@ import org.apache.commons.lang.text.StrSubstitutor; import org.apache.commons.lang.text.StrTokenizer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; @@ -73,9 +75,17 @@ public abstract class AbstractWebAppContextLoader extends AbstractAppContextLoad AppContext.Internals.startContext(); log.info("AppContext initialized"); - } catch (Throwable e) { + } catch (RuntimeException e) { log.error("Error initializing application", e); - throw new RuntimeException(e); + try { + ApplicationContext springContext = AppContext.getApplicationContext(); + if (springContext != null) { + ((ConfigurableApplicationContext) springContext).close(); + } + } catch (Exception e1) { + log.debug("Error closing application context: {}", e1.toString()); + } + throw e; } } diff --git a/modules/global/src/com/haulmont/cuba/core/sys/CubaThreadPoolTaskScheduler.java b/modules/global/src/com/haulmont/cuba/core/sys/CubaThreadPoolTaskScheduler.java index 33e1596051..75a41eac61 100644 --- a/modules/global/src/com/haulmont/cuba/core/sys/CubaThreadPoolTaskScheduler.java +++ b/modules/global/src/com/haulmont/cuba/core/sys/CubaThreadPoolTaskScheduler.java @@ -56,6 +56,7 @@ public class CubaThreadPoolTaskScheduler extends ThreadPoolTaskScheduler impleme Thread.sleep(200); } catch (InterruptedException e) { t.interrupt(); + return; } } super.beforeExecute(t, r); diff --git a/modules/global/src/com/haulmont/cuba/core/sys/DBNotInitializedException.java b/modules/global/src/com/haulmont/cuba/core/sys/DbInitializationException.java similarity index 60% rename from modules/global/src/com/haulmont/cuba/core/sys/DBNotInitializedException.java rename to modules/global/src/com/haulmont/cuba/core/sys/DbInitializationException.java index 779ad2e17a..b5a3216a8f 100644 --- a/modules/global/src/com/haulmont/cuba/core/sys/DBNotInitializedException.java +++ b/modules/global/src/com/haulmont/cuba/core/sys/DbInitializationException.java @@ -1,27 +1,39 @@ -/* - * Copyright (c) 2008-2016 Haulmont. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package com.haulmont.cuba.core.sys; - -public class DBNotInitializedException extends Exception { - public DBNotInitializedException() { - } - - public DBNotInitializedException(String message) { - super(message); - } +/* + * Copyright (c) 2008-2016 Haulmont. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.haulmont.cuba.core.sys; + +public class DbInitializationException extends Exception { + + private boolean retryPossible; + + public DbInitializationException() { + } + + public DbInitializationException(String message) { + super(message); + } + + public DbInitializationException(boolean retryPossible, String message, Throwable cause) { + super(message, cause); + this.retryPossible = retryPossible; + } + + public boolean isRetryPossible() { + return retryPossible; + } } \ No newline at end of file diff --git a/modules/global/src/com/haulmont/cuba/core/sys/DbUpdater.java b/modules/global/src/com/haulmont/cuba/core/sys/DbUpdater.java index e3a9ce96ee..2c6481698a 100644 --- a/modules/global/src/com/haulmont/cuba/core/sys/DbUpdater.java +++ b/modules/global/src/com/haulmont/cuba/core/sys/DbUpdater.java @@ -21,7 +21,9 @@ import java.util.List; public interface DbUpdater { String NAME = "cuba_DbUpdater"; - void updateDatabase(); + boolean dbInitialized() throws DbInitializationException; - List findUpdateDatabaseScripts() throws DBNotInitializedException; + void updateDatabase() throws DbInitializationException; + + List findUpdateDatabaseScripts() throws DbInitializationException; } \ No newline at end of file