SuperX-Kernmodul
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

301 lines
14 KiB

package de.superx.spring;
import static java.util.Map.entry;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import org.apache.log4j.Logger;
import org.springframework.core.io.ClassPathResource;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import de.superx.servlet.SuperXManager;
import de.superx.spring.config.BatchConfig;
import de.superx.util.PathAndFileUtils;
public class HIS1Databases {
public static final String HIS1_DATABASES_PROPERTIES = "HIS1_DATABASES_PROPERTIES";
private static final byte[] KEY_DATA = { -108, -50, -5, -75, -98, 28, -116, 107 };
private static final SecretKeySpec KEY = new SecretKeySpec(KEY_DATA, "DES");
private static Cipher desCipher;
private static Logger logger = Logger.getLogger(HIS1Databases.class);
private HIS1Databases() { //Mit Ausnahmegenehmigung
//Prevent instantiation
}
public static Map<String,Map<String,String>> getAllConnections() throws
InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException{
return fetchConnections();
}
public static Map<String,Map<String,String>> getValidConnections() throws
InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException{
Map<String,Map<String,String>> conns = getAllConnections();
logger.info("Got connections: " + conns.keySet());
Map<String,Map<String,String>> validConns = new HashMap<>();
for (String key : conns.keySet()) {
Map<String,String> connectionProperties = conns.get(key);
if (
connectionProperties.get(DbPropertyName.database.name()) != null
&&connectionProperties.get(DbPropertyName.user.name()) != null
&& connectionProperties.get(DbPropertyName.url.name()) != null
&& connectionProperties.get(DbPropertyName.protocol.name()) != null
&& connectionProperties.get(DbPropertyName.jdbcDriver.name()) != null
) {
logger.info("Valid connection: " + key);
validConns.put(key, connectionProperties);
} else {
logger.warn("Invalid connection: " + key);
}
}
return validConns;
}
public static Map<String,String> getConnection(String connectionName) throws
InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException{
Map<String,Map<String,String>> connections = fetchConnections();
return connections.get(connectionName) != null ? connections.get(connectionName) : new HashMap<>();
}
private static Properties fetchProperties() {
Properties props = new Properties();
String databasesPropertiesFileName = System.getProperty(HIS1_DATABASES_PROPERTIES);
if (databasesPropertiesFileName != null) {
File databasePropertiesFile = new File(databasesPropertiesFileName);
if (databasePropertiesFile.exists()) {
logger.info("Reading database properties from " + databasesPropertiesFileName);
try {
props.load(new FileInputStream(databasePropertiesFile));
} catch (Exception e) {
logger.error("Couldn't load database properties from " + databasesPropertiesFileName,e);
}
} else {
logger.error("HIS1_DATABASES_PROPERTIES file not found: " + databasesPropertiesFileName);
}
}
if (props.size() == 0) {
// no external properties file, loading from classpath
File propFile = new File(PathAndFileUtils.getWebinfPath() + File.separator + "conf" + File.separator + "his1_databases.properties");
if (!propFile.exists()) {
logger.warn("No his1_databasees.properties found. Will try to load from db.properties!");
return readDbProperties();
}
try(FileInputStream s = new FileInputStream(propFile)){
props.load(s);
} catch (IOException e1) {
logger.fatal(String.format("Unable to access his1_databases file: %s",e1.getMessage()));
e1.printStackTrace();
}
}
Properties manualConfig = manualConfiguration();
return mergeProperties(props, manualConfig);
}
private static Properties manualConfiguration() {
Properties optProps = new Properties();
File poolConf = new File(PathAndFileUtils.getWebinfPath() + File.separator + "conf" + File.separator + "his1_db_poolsize.properties");
try(FileInputStream s = new FileInputStream(poolConf)){
optProps.load(s);
} catch (IOException e1) {
logger.info("Unable to access manual poolsize configuration\nSkipping manual poolsize configuration - setting defaults: "+e1.getMessage());
}
return optProps;
}
private static Properties mergeProperties(Properties... properties) {
return Stream.of(properties).collect(Properties::new, Map::putAll, Map::putAll);
}
private static Cipher getDesCipher() throws NoSuchAlgorithmException, NoSuchPaddingException {
return Cipher.getInstance("DES");
}
private static String decryptDbPassword(String password) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
if(password == null || password.equals("")) {
return null;
}else if(!password.startsWith("sx_des")) {
return password;
}
if(desCipher == null) {
desCipher = getDesCipher();
}
String[] stext = password.substring(7).split("#");
byte[] bstext = new byte[stext.length];
for(int i = 0; i<stext.length;i++) {
bstext[i] = Byte.parseByte(stext[i]);
}
desCipher.init(Cipher.DECRYPT_MODE, KEY);
byte[] decrypted = desCipher.doFinal(bstext);
return new String(decrypted, StandardCharsets.UTF_8);
}
private static Map<String,Map<String,String>> fetchConnections() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
Map<String,Map<String,String>> connections = new HashMap<>();
Properties props = fetchProperties();
String[] dbconnections = props.get("dbconnections").toString().split(",");
final Set<String> propertyNames = props.stringPropertyNames();
for (String s : dbconnections) {
Map<String,String> properties = new HashMap<>();
logger.info(String.format("%s connection retrieved from HISinOne", s));
List<String> keys = propertyNames.stream().filter(it -> it.startsWith("db." + s + ".")).collect(Collectors.toList());
for (String key : keys) {
String value = props.getProperty(key);
if (key.endsWith("password")) {
value = decryptDbPassword(value.trim());
}
properties.put(key.substring(key.lastIndexOf('.') + 1), value);
}
connections.put(s, properties);
}
return connections;
}
private static Properties readDbProperties() {
Properties properties = new Properties();
String webinfpfad=PathAndFileUtils.getWebinfPath();
File dbPropertiesFile = new File( webinfpfad
+ File.separator
+ "db.properties");
if (dbPropertiesFile.exists()) {
try {
Properties dbProperties = new Properties();
dbProperties.load(new FileReader(dbPropertiesFile));
properties = convertDbProperties(dbProperties);
} catch (Exception e) {
throw new RuntimeException("Couldn't read db.properties!", e);
}
} else {
throw new RuntimeException("Couldn't read " + dbPropertiesFile.getAbsolutePath() + " because file doesn't exist!");
}
return properties;
}
public static Properties convertDbProperties(Properties dbProperties) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
Properties his1Properties = new Properties();
String jdbcUrl = dbProperties.getProperty("connectionURL");
String user = dbProperties.getProperty("connectionName");
String password = decryptDbPassword(dbProperties.getProperty("connectionPassword"));
String sslmode = dbProperties.getProperty("sslmode");
String driver = dbProperties.getProperty("driverName");
String[] urlParts = analyzeJdbcUrl(jdbcUrl);
String prefix = "db.eduetl.";
Map<String,String> config = Map.ofEntries(
entry(prefix + DbPropertyName.user.name(), user),
entry(prefix + DbPropertyName.password.name(), password),
entry(prefix + DbPropertyName.jdbcDriver.name(), driver),
entry(prefix + DbPropertyName.ssl.name(), sslmode != null && sslmode.equalsIgnoreCase("require") ? "true" : "false"),
entry(prefix + DbPropertyName.protocol.name(), urlParts[0]),
entry(prefix + DbPropertyName.host.name(), urlParts[1]),
entry(prefix + DbPropertyName.dbserverport.name(), urlParts[2]),
entry(prefix + DbPropertyName.database.name(), urlParts[3]),
entry(prefix + DbPropertyName.url.name(), jdbcUrl),
entry(DbPropertyName.dbconnections.name(), "eduetl")
);
his1Properties.putAll(config);
return his1Properties;
}
public static String[] analyzeJdbcUrl(String jdbcUrl) {
int protocolEndIndex = jdbcUrl.indexOf("//");
int portBeginIndex = jdbcUrl.indexOf(":", protocolEndIndex);
int dbBeginIndex = jdbcUrl.indexOf("/", portBeginIndex);
int dbEndIndex = jdbcUrl.indexOf("?"); String protocol = jdbcUrl.substring(0, protocolEndIndex);
String host = jdbcUrl.substring(protocolEndIndex + 2, portBeginIndex);
String port = jdbcUrl.substring(portBeginIndex + 1, dbBeginIndex);
String db = jdbcUrl.substring(dbBeginIndex + 1, dbEndIndex == -1 ? jdbcUrl.length() : dbEndIndex);
return new String[] {
protocol, host, port, db
};
}
public static HikariDataSource convertConnection(String connectionName, Map<String,String> co, long leakDetectionThreshold, long connectionTimeout){
HikariConfig conf = new HikariConfig();
conf.setUsername(co.get(DbPropertyName.user.name()));
conf.setPassword(co.get(DbPropertyName.password.name()));
conf.setDriverClassName(co.get(DbPropertyName.jdbcDriver.name()));
conf.setJdbcUrl(co.get(DbPropertyName.url.name()));
if (co.get(DbPropertyName.pgschema.name()) != null) {
conf.setSchema(co.get(DbPropertyName.pgschema.name()));
} else if ("hisinone".equals(connectionName)) {
conf.setSchema("hisinone");
}
if (co.get(DbPropertyName.reconnectAfter.name()) != null) {
// Angabe reconnectAfter in der databases.xml ist in Minuten!
conf.setMaxLifetime(Long.parseLong(co.get(DbPropertyName.reconnectAfter.name())) * 60000);
}
conf.setPoolName(connectionName);
conf.setLeakDetectionThreshold(leakDetectionThreshold);
conf.setConnectionTimeout(connectionTimeout);
// in HISinOne-BI context: Set PostgreSQL variables to be consistent with
// HISinOne itself (which also does some settings - see "DbHandler")
if (co.get(DbPropertyName.jdbcDriver.name()).equals(org.postgresql.Driver.class.getCanonicalName()) &&
System.getProperty(SuperXManager.SUPER_X_HISINONE_VERSION) != null) {
conf.setConnectionInitSql("SET standard_conforming_strings TO 'off'; SET datestyle TO 'ISO, DMY';");
}
String minConnectionsStr = co.get("poolsize_min");
String maxConnectionsStr = co.get("poolsize_max");
int defaultPoolSize = "eduetl".equals(connectionName) ? BatchConfig.EDUETL_POOL_SIZE : BatchConfig.DEFAULT_POOL_SIZE;
int maxConnections = maxConnectionsStr != null ? Integer.parseInt(maxConnectionsStr) : defaultPoolSize;
int minConnections = minConnectionsStr != null ? Integer.parseInt(minConnectionsStr) : maxConnections;
conf.setMinimumIdle(minConnections);
logger.info(String.format("HikariPool for %s initialized with a minimum of %s idle connections.", connectionName, Integer.toString(minConnections)));
conf.setMaximumPoolSize(maxConnections);
logger.info(String.format("HikariPool for %s initialized with a maximum of %s connections", connectionName, Integer.toString(maxConnections)));
addDataSourceProperties(conf, co);
conf.setExceptionOverrideClassName("de.superx.db.HikariSqlExceptionOverride");
HikariDataSource ds = null;
try {
ds = new HikariDataSource(conf);
} catch(Exception ex) {
logger.error("Couldn't create Hikaripool for " + connectionName, ex);
}
return ds;
}
private static void addDataSourceProperties(HikariConfig conf, Map<String, String> co) {
for (Entry<String,String> e : co.entrySet()) {
if (!DbPropertyName.isStandard(e.getKey())) {
conf.addDataSourceProperty(e.getKey(), e.getValue());
logger.debug("Set additional db property " + e.getKey() + " to " + e.getValue());
}
}
conf.addDataSourceProperty("ApplicationName", "HIS-BI");
}
}