package com.forgeessentials.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.HashMap; import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Property; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Level; import com.forgeessentials.util.output.LoggingHandler; import com.google.common.base.Throwables; public class DBConnector { EnumDBType loadedType; private final DBConnector fallback; public final String name; private final EnumDBType dType; private EnumDBType type; private EnumDBType tempType; private final String dbDefault; private final String dbFileDefault; private boolean useParent; private HashMap<EnumDBType, HashMap<String, Property>> data; /** * @param name * a name for the DB connector. to be used in Logging. * @param fallback * The DBConnector from which to take information for a given type if loading that type from this config * fails. * @param dType * the default database type to use * @param dbDefault * the default name for remote databases * @param dbFileDefault * the default path for file databases. Relative to FEDIR * @paramuseFallbac if the Fallback should be used for remote Databases */ public DBConnector(String name, DBConnector fallback, EnumDBType dType, String dbDefault, String dbFileDefault, boolean useFallback) { this.name = name; this.fallback = fallback; this.dType = type = tempType = dType; this.dbDefault = dbDefault; this.dbFileDefault = dbFileDefault; data = new HashMap<EnumDBType, HashMap<String, Property>>(); useParent = useFallback; } /** * Forcibly writes everything to the config. the config's save() method is not called. * * @param config * @param category * the category where everything regarding this connector will be. */ public void write(Configuration config, String cat) { config.get(cat, "chosenType", dType.toString(), " valid types: " + StringUtils.join(EnumDBType.values(), ", ")).set(type.toString()); if (fallback != null) { config.get(cat, "checkParent", useParent, "If this is true, settings will be taken from the parent, most probably the Main or Core config. This is only taken into effect with remote databases.") .set(useParent); } String newcat; HashMap<String, Property> props; for (EnumDBType dbType : EnumDBType.values()) { newcat = cat + "." + dbType; props = data.get(dbType); if (props == null) { continue; } if (dbType.isRemote) { config.get(newcat, "host", "localhost").set(props.get("host").getString()); config.get(newcat, "port", 3360).set(props.get("port").getString()); config.get(newcat, "database", dbDefault).set(props.get("database").getString()); config.get(newcat, "user", "FEUSER").set(props.get("user").getString()); config.get(newcat, "pass", "password").set(props.get("pass").getString()); } else { config.get(newcat, "database", dbFileDefault, "this may be a file path as well.").set(props.get("database").getString()); } } } /** * Loads the the connector from the config for use. config load method is not called. * * @param config * @param category * the category where everything regarding this connector will be. */ public void loadOrGenerate(Configuration config, String cat) { try { tempType = type = EnumDBType.valueOf(config.get(cat, "chosenType", dType.toString()).getString()); if (fallback != null) { useParent = config.get(cat, "checkParent", false).getBoolean(false); } } catch (Exception e) { tempType = type = dType; // reset to default.. } String newcat; HashMap<String, Property> props; for (EnumDBType dbType : EnumDBType.values()) { newcat = cat + "." + dbType; props = data.get(dbType); if (props == null) { props = new HashMap<String, Property>(); data.put(dbType, props); } if (dbType.isRemote) { props.put("host", config.get(newcat, "host", "localhost")); props.put("port", config.get(newcat, "port", 3306)); props.put("database", config.get(newcat, "database", dbDefault)); props.put("user", config.get(newcat, "user", "FEUSER")); props.put("pass", config.get(newcat, "pass", "password")); } else { props.put("database", config.get(newcat, "database", dbFileDefault, "this may be a file path as well.")); } } config.get(cat, "chosenType", type.toString(), " valid types: " + StringUtils.join(EnumDBType.values(), ", ")); config.get(cat, "checkParent", useParent, "If this is true, settings will be taken from the parent, most probably the Main or Core config. This is only taken into effect with remote databases."); } public Connection getChosenConnection() { HashMap<String, Property> props; Connection con; try { props = data.get(type); if (type.isRemote) { // check fallback if (useParent) { con = fallback.getSpecificConnection(type); if (con != null) { return con; } else { LoggingHandler.felog.warn("[FE+SQL] " + name + " Parent check failed, going to in-house."); } } // continue with stuff String host = props.get("host").getString(); int port = props.get("port").getInt(); String database = props.get("database").getString(); String user = props.get("user").getString(); String pass = props.get("pass").getString(); type.loadClass(); String connect = type.getConnectionString(host, port, database); con = DriverManager.getConnection(connect, user, pass); return con; } else { // nonremote connections String database = props.get("database").getString(); String connect = type.getConnectionString(database); con = DriverManager.getConnection(connect); return con; } } catch (Exception e) { LoggingHandler.felog.log(Level.WARN, "[FE+SQL] " + name + " In-House check failed, going to default.", e); } try { tempType = dType; // try the default... props = data.get(dType); if (dType.isRemote) { // continue with stuff String host = props.get("host").getString(); int port = props.get("port").getInt(); String database = props.get("database").getString(); String user = props.get("user").getString(); String pass = props.get("pass").getString(); dType.loadClass(); String connect = dType.getConnectionString(host, port, database); return DriverManager.getConnection(connect, user, pass); } else { // nonremote connections String database = props.get("database").getString(); String connect = dType.getConnectionString(database); return DriverManager.getConnection(connect); } } catch (SQLException e) { LoggingHandler.felog.error("[FE+SQL] " + name + " CATASTROPHIC DATABASE CONNECTION FAILIURE!!!"); Throwables.propagate(e); } return null; } /** * @param dbType * Only use this for remote types. * @return NULL if some error occurred. * @throws IllegalArgumentException * if the type is not remote */ private Connection getSpecificConnection(EnumDBType dbType) throws IllegalArgumentException { if (!dbType.isRemote) { throw new IllegalArgumentException("Non remote type " + dbType + " is asking for parent config!"); } try { HashMap<String, Property> props = data.get(dbType); String host = props.get("host").getString(); int port = props.get("port").getInt(); String database = props.get("database").getString(); String user = props.get("user").getString(); String pass = props.get("pass").getString(); dbType.loadClass(); String connect = dbType.getConnectionString(host, port, database); return DriverManager.getConnection(connect, user, pass); } catch (Exception e) { LoggingHandler.felog.error("[FE+SQL] " + name + " Failed parent check: " + e); return null; } } public EnumDBType getActiveType() { return tempType; } }