PL-8744 Handling of database connection problems on middleware startup

This commit is contained in:
Konstantin Krivopustov 2017-03-29 17:37:26 +04:00
parent d24fb2fdd5
commit 5b6bc43a15
11 changed files with 137 additions and 78 deletions

View File

@ -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());
}
}

View File

@ -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

View File

@ -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);

View File

@ -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);
}
}
}
}
}

View File

@ -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<String> findUpdateDatabaseScripts() throws DBNotInitializedException {
public List<String> findUpdateDatabaseScripts() throws DbInitializationException {
List<String> 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<ScriptResource> 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 {

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -56,6 +56,7 @@ public class CubaThreadPoolTaskScheduler extends ThreadPoolTaskScheduler impleme
Thread.sleep(200);
} catch (InterruptedException e) {
t.interrupt();
return;
}
}
super.beforeExecute(t, r);

View File

@ -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;
}
}

View File

@ -21,7 +21,9 @@ import java.util.List;
public interface DbUpdater {
String NAME = "cuba_DbUpdater";
void updateDatabase();
boolean dbInitialized() throws DbInitializationException;
List<String> findUpdateDatabaseScripts() throws DBNotInitializedException;
void updateDatabase() throws DbInitializationException;
List<String> findUpdateDatabaseScripts() throws DbInitializationException;
}