use of soc.server.database.DBSettingMismatchException in project JSettlers2 by jdmonin.
the class SOCServer method initSocServer.
/**
* Common init for all constructors.
* Prints some progress messages to {@link System#err}.
* Sets game option default values via {@link #init_propsSetGameopts(Properties)}.
* Calls {@link SOCMessageDispatcher#setServer(SOCServer, SOCGameListAtServer)}.
* Starts all server threads except the main thread, unless constructed in Utility Mode
* ({@link #hasUtilityModeProp}).
* If {@link #PROP_JSETTLERS_STARTROBOTS} is specified, those aren't started until {@link #serverUp()}.
*<P>
* If there are problems with the network setup ({@link #error} != null),
* this method will throw {@link SocketException}.
*<P>
* If problems running a {@link SOCDBHelper#PROP_JSETTLERS_DB_SCRIPT_SETUP db setup script}
* or {@link SOCDBHelper#PROP_JSETTLERS_DB_UPGRADE__SCHEMA schema upgrade},
* this method will throw {@link SQLException}.
*<P>
* If we can't connect to a database, but it looks like we need one (because
* {@link SOCDBHelper#PROP_JSETTLERS_DB_URL}, {@link SOCDBHelper#PROP_JSETTLERS_DB_DRIVER}
* or {@link SOCDBHelper#PROP_JSETTLERS_DB_JAR} is specified in {@code props}),
* or there are other problems with DB-related contents of {@code props}
* (see {@link SOCDBHelper#initialize(String, String, Properties)}; exception's {@link Throwable#getCause()}
* will be an {@link IllegalArgumentException} or {@link DBSettingMismatchException}) this method will
* print details to {@link System#err} and throw {@link SQLException}.
*
*<H5>Utility Mode</H5>
* If a db setup script runs successfully, or {@code props} contains the password reset parameter
* {@link SOCDBHelper#PROP_IMPL_JSETTLERS_PW_RESET}, the server does not complete its startup;
* this method will set {@link #hasUtilityModeProp} and (only for setup script) throw {@link EOFException}.
*<P>
* For the password reset parameter, the caller will need to prompt for and change the password;
* this method will not do that.
*
* @param databaseUserName Used for DB connect - not retained
* @param databasePassword Used for DB connect - not retained
* @throws SocketException If a network setup problem occurs
* @throws EOFException If db setup script ran successfully and server should exit now;
* thrown in Utility Mode ({@link #hasUtilityModeProp}).
* @throws SQLException If db setup script fails, or need db but can't connect
* @throws IllegalArgumentException If {@code props} contains game options ({@code jsettlers.gameopt.*})
* with bad syntax. See {@link #PROP_JSETTLERS_GAMEOPT_PREFIX} for expected syntax.
* See {@link #parseCmdline_DashedArgs(String[])} for how game option properties are checked.
* Also thrown if {@link SOCDBHelper#PROP_JSETTLERS_DB_UPGRADE__SCHEMA} flag
* is set, but {@link SOCDBHelper#isSchemaLatestVersion()}. {@link Throwable#getMessage()} will have
* problem details for any {@code IllegalArgumentException} thrown here.
* @throws IllegalStateException If {@link Version#versionNumber()} returns 0 (packaging error)
* @since 1.1.00
*/
private void initSocServer(String databaseUserName, String databasePassword) throws SocketException, EOFException, SQLException, IllegalArgumentException, IllegalStateException {
Version.printVersionText(System.err, "Java Settlers Server ");
if (Version.versionNumber() == 0) {
throw new IllegalStateException("Packaging error: Cannot determine JSettlers version");
}
/**
* If true, connect to DB (like validate_config_mode) but start no threads.
* Will run the requested tests and exit.
*/
final boolean test_mode_with_db = getConfigBoolProperty(PROP_JSETTLERS_TEST_DB, false);
final boolean validate_config_mode = getConfigBoolProperty(PROP_JSETTLERS_TEST_VALIDATE__CONFIG, false);
final boolean wants_upg_schema = getConfigBoolProperty(SOCDBHelper.PROP_JSETTLERS_DB_UPGRADE__SCHEMA, false);
boolean db_test_bcrypt_mode = false;
final String val = props.getProperty(SOCDBHelper.PROP_JSETTLERS_DB_BCRYPT_WORK__FACTOR);
if (val != null) {
db_test_bcrypt_mode = (val.equalsIgnoreCase("test"));
if (db_test_bcrypt_mode)
// make sure DBH.initialize won't try to parse "test" as an integer
props.remove(SOCDBHelper.PROP_JSETTLERS_DB_BCRYPT_WORK__FACTOR);
}
// Set this flag as early as possible
hasUtilityModeProp = validate_config_mode || test_mode_with_db || wants_upg_schema || db_test_bcrypt_mode || (null != props.getProperty(SOCDBHelper.PROP_JSETTLERS_DB_SCRIPT_SETUP)) || props.containsKey(SOCDBHelper.PROP_JSETTLERS_DB_SETTINGS) || (null != props.getProperty(SOCDBHelper.PROP_IMPL_JSETTLERS_PW_RESET));
if (test_mode_with_db)
System.err.println("* DB Test Mode: Will run tests and exit.");
if (validate_config_mode)
System.err.println("* Config Validation Mode: Checking configuration and exiting.");
/* Check for problems during super setup (such as port already in use).
* Ignore net errors if we're running a DB setup script and then exiting.
*/
if ((error != null) && !hasUtilityModeProp) {
final String errMsg = "* Exiting due to network setup problem: " + error.toString();
throw new SocketException(errMsg);
}
if (!props.containsKey(PROP_JSETTLERS_STARTROBOTS))
props.setProperty(PROP_JSETTLERS_STARTROBOTS, Integer.toString(SOC_STARTROBOTS_DEFAULT));
// Set game option defaults from any jsettlers.gameopt.* properties found.
// If problems found, throws IllegalArgumentException with details.
// Does not apply scenario's game options, if any.
// Ignores unknown scenario ("SC"), see init_checkScenarioOpts for that.
init_propsSetGameopts(props);
int v = getConfigIntProperty(PROP_JSETTLERS_BOTS_FAST__PAUSE__PERCENT, -1);
if (v != -1) {
if ((v >= 0) && (v <= 100))
SOCRobotBrain.BOTS_ONLY_FAST_PAUSE_FACTOR = .01f * v;
else
throw new IllegalArgumentException("Error: Property out of range (0 to 100): " + PROP_JSETTLERS_BOTS_FAST__PAUSE__PERCENT);
}
((SOCMessageDispatcher) inboundMsgDispatcher).setServer(this, srvMsgHandler, gameList);
if (allowDebugUser) {
System.err.println("Warning: Remote debug commands are allowed.");
}
/**
* See if the user specified a non-random robot cookie value.
*/
if (props.containsKey(PROP_JSETTLERS_BOTS_COOKIE)) {
final String cook = props.getProperty(PROP_JSETTLERS_BOTS_COOKIE).trim();
if (cook.length() > 0) {
if (SOCMessage.isSingleLineAndSafe(cook)) {
robotCookie = cook;
} else {
final String errmsg = "Error: The robot cookie value (param " + PROP_JSETTLERS_BOTS_COOKIE + ") can't contain comma or pipe characters.";
System.err.println(errmsg);
throw new IllegalArgumentException(errmsg);
}
}
// else robotCookie remains null
} else {
robotCookie = generateRobotCookie();
}
final boolean accountsRequired = getConfigBoolProperty(PROP_JSETTLERS_ACCOUNTS_REQUIRED, false);
/**
* Try to connect to the DB, if any.
* Running SOCDBHelper.initialize(..) will handle some Utility Mode properties
* like PROP_JSETTLERS_DB_SETTINGS if present.
*/
boolean db_err_printed = false;
try {
SOCDBHelper.initialize(databaseUserName, databasePassword, props);
features.add(SOCServerFeatures.FEAT_ACCTS);
System.err.println("User database initialized.");
if (props.getProperty(SOCDBHelper.PROP_JSETTLERS_DB_SCRIPT_SETUP) != null) {
// the sql script was ran by initialize
final String msg = "DB setup script successful";
utilityModeMessage = msg;
throw new EOFException(msg);
}
// set some DB-related SOCServer fields: acctsNotOpenRegButNoUsers, databaseUserAdmins
initSocServer_dbParamFields(accountsRequired, wants_upg_schema);
// check schema version, upgrade if requested:
if (!SOCDBHelper.isSchemaLatestVersion()) {
if (wants_upg_schema) {
try {
SOCDBHelper.upgradeSchema(databaseUserAdmins);
String msg = "DB schema upgrade was successful";
if (SOCDBHelper.doesSchemaUpgradeNeedBGTasks())
msg += "; some upgrade tasks will complete in the background during normal server operation";
utilityModeMessage = msg;
throw new EOFException(msg);
} catch (EOFException e) {
throw e;
} catch (Exception e) {
db_err_printed = true;
if (e instanceof MissingResourceException)
System.err.println("* To begin schema upgrade, please fix and rerun: " + e.getMessage());
else
System.err.println(e);
if (e instanceof SQLException) {
throw (SQLException) e;
} else {
SQLException sqle = new SQLException("Error during DB schema upgrade");
sqle.initCause(e);
throw sqle;
}
}
} else {
System.err.println("\n* Database schema upgrade is recommended: To upgrade, use -D" + SOCDBHelper.PROP_JSETTLERS_DB_UPGRADE__SCHEMA + "=Y command line flag.\n");
}
} else if (wants_upg_schema) {
db_err_printed = true;
final String errmsg = "* Cannot upgrade database schema: Already at latest version";
System.err.println(errmsg);
throw new IllegalArgumentException(errmsg);
}
// reminder: if props.getProperty(SOCDBHelper.PROP_IMPL_JSETTLERS_PW_RESET),
// caller will need to prompt for and change the password
} catch (// just a warning at this point; other code checks if db failed but is required
SQLException sqle) {
if (wants_upg_schema && db_err_printed) {
throw sqle;
}
System.err.println("Warning: No user database available: " + sqle.getMessage());
Throwable cause = sqle.getCause();
while ((cause != null) && !(cause instanceof ClassNotFoundException)) {
System.err.println("\t" + cause);
cause = cause.getCause();
}
if (wants_upg_schema || (props.getProperty(SOCDBHelper.PROP_JSETTLERS_DB_SCRIPT_SETUP) != null)) {
throw sqle;
}
String propReqDB = null;
if (accountsRequired)
propReqDB = PROP_JSETTLERS_ACCOUNTS_REQUIRED;
else if (props.containsKey(PROP_JSETTLERS_ACCOUNTS_ADMINS))
propReqDB = PROP_JSETTLERS_ACCOUNTS_ADMINS;
if (propReqDB != null) {
final String errMsg = "* Property " + propReqDB + " requires a database.";
System.err.println(errMsg);
System.err.println("\n* Exiting because current startup properties specify a database.");
throw new SQLException(errMsg);
}
if (props.containsKey(SOCDBHelper.PROP_JSETTLERS_DB_URL) || props.containsKey(SOCDBHelper.PROP_JSETTLERS_DB_JAR) || props.containsKey(SOCDBHelper.PROP_JSETTLERS_DB_DRIVER)) {
// If other db props were asked for, the user is expecting a DB.
// So, fail instead of silently continuing without it.
System.err.println("* Exiting because current startup properties specify a database.");
throw sqle;
}
if (props.containsKey(SOCDBHelper.PROP_IMPL_JSETTLERS_PW_RESET)) {
System.err.println("* Exiting because --pw-reset requires a database.");
throw sqle;
}
System.err.println("Users will not be authenticated.");
} catch (// successfully ran script or schema upgrade, signal to exit
EOFException eox) {
throw eox;
} catch (// error from requested script
IOException iox) {
System.err.println("\n* Could not run database setup script: " + iox.getMessage());
Throwable cause = iox.getCause();
while ((cause != null) && !(cause instanceof ClassNotFoundException)) {
System.err.println("\t" + cause);
cause = cause.getCause();
}
try {
SOCDBHelper.cleanup(true);
} catch (SQLException x) {
}
SQLException sqle = new SQLException("Error running DB setup script");
sqle.initCause(iox);
throw sqle;
} catch (IllegalArgumentException iax) {
System.err.println("\n* Error in specified database properties: " + iax.getMessage());
SQLException sqle = new SQLException("Error with DB props");
sqle.initCause(iax);
throw sqle;
} catch (DBSettingMismatchException dx) {
// initialize(..) already printed details to System.err
System.err.println("\n* Mismatch between database settings and specified properties");
SQLException sqle = new SQLException("DB settings mismatch");
sqle.initCause(dx);
throw sqle;
}
if (db_test_bcrypt_mode)
SOCDBHelper.testBCryptSpeed();
if (hasUtilityModeProp && !(test_mode_with_db || validate_config_mode)) {
// <--- don't continue startup if Utility Mode ---
return;
}
if (SOCDBHelper.isInitialized()) {
if (accountsRequired)
System.err.println("User database accounts are required for all players.");
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=38016 "WONTFIX/README" since 2007-07-18
try {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
System.err.println("\n--\n-- shutdown; disconnecting from db --\n--\n");
System.err.flush();
try {
// Before disconnect, do a final check for unexpected DB settings changes
try {
SOCDBHelper.checkSettings(true, false);
} catch (Exception x) {
}
SOCDBHelper.cleanup(true);
} catch (SQLException x) {
}
}
});
} catch (Throwable th) {
// just a warning
System.err.println("Warning: Could not register shutdown hook for database disconnect. Check java security settings.");
}
}
startTime = System.currentTimeMillis();
numberOfGamesStarted = 0;
numberOfGamesFinished = 0;
numberOfUsers = 0;
clientPastVersionStats = new HashMap<Integer, Integer>();
numRobotOnlyGamesRemaining = getConfigIntProperty(PROP_JSETTLERS_BOTS_BOTGAMES_TOTAL, 0);
if (numRobotOnlyGamesRemaining > 0) {
final int n = SOCGame.MAXPLAYERS_STANDARD;
if (n > getConfigIntProperty(PROP_JSETTLERS_STARTROBOTS, 0)) {
final String errmsg = ("*** To start robot-only games, server needs at least " + n + " robots started.");
System.err.println(errmsg);
throw new IllegalArgumentException(errmsg);
}
}
if (CLIENT_MAX_CREATE_CHANNELS != 0)
features.add(SOCServerFeatures.FEAT_CHANNELS);
/**
* Start various threads.
*/
if (!(test_mode_with_db || validate_config_mode)) {
serverRobotPinger = new SOCServerRobotPinger(this, robots);
serverRobotPinger.start();
gameTimeoutChecker = new SOCGameTimeoutChecker(this);
gameTimeoutChecker.start();
}
this.databaseUserName = databaseUserName;
this.databasePassword = databasePassword;
/**
* Print game options if we've set them on commandline, or if
* any option defaults require a minimum client version.
*/
if (hasSetGameOptions || validate_config_mode || (SOCVersionedItem.itemsMinimumVersion(SOCGameOption.getAllKnownOptions()) > -1)) {
// wait for other output to appear first
Thread.yield();
try {
Thread.sleep(200);
} catch (InterruptedException ie) {
}
printGameOptions();
}
if (getConfigBoolProperty(PROP_JSETTLERS_BOTS_SHOWCOOKIE, false))
System.err.println("Robot cookie: " + robotCookie);
if (validate_config_mode) {
// Print configured known properties (ignore if not in PROPS_LIST);
// this also gives them in the same order as PROPS_LIST,
// which is the same order --help prints them out.
System.err.println();
System.err.println("-- Configured server properties: --");
for (int i = 0; i < PROPS_LIST.length; i += 2) {
final String pkey = PROPS_LIST[i];
if ((!pkey.equals(PROP_JSETTLERS_TEST_VALIDATE__CONFIG)) && props.containsKey(pkey))
System.err.format("%-40s %s\n", pkey, props.getProperty(pkey));
}
System.err.println();
System.err.println("* Config Validation Mode: No problems found.");
}
if (test_mode_with_db) {
// failures/errors throw SQLException for our caller to catch
SOCDBHelper.testDBHelper();
}
if (!(test_mode_with_db || validate_config_mode)) {
System.err.print("The server is ready.");
if (port > 0)
System.err.print(" Listening on port " + port);
System.err.println();
if (SOCDBHelper.isInitialized() && SOCDBHelper.doesSchemaUpgradeNeedBGTasks())
// includes 5-second sleep before conversions begin
SOCDBHelper.startSchemaUpgradeBGTasks();
}
System.err.println();
}
Aggregations