/**
* This file is part of alf.io.
*
* alf.io is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* alf.io is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with alf.io. If not, see <http://www.gnu.org/licenses/>.
*/
package alfio.config.support;
import org.springframework.core.env.Environment;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Objects;
import static java.util.Optional.ofNullable;
/**
* For handling the various differences between the cloud providers.
* <p>
* Supported:
* - Openshift : pgsql only
* - ElephantDB: runs on Openshift and Cloud Foundry
* - Cloud Foundry: postgres, mysql (injected)
* - Heroku
* - AWS: mysql and postgres
* - local use with system properties
*/
public enum PlatformProvider {
DEFAULT,
//see
// https://developers.openshift.com/external-services/elephantsql.html
// http://docs.run.pivotal.io/marketplace/services/elephantsql.html
ELEPHANTSQL {
@Override
public String getUrl(Environment env) {
if(isCloudFoundry(env)) {
return "";
}
URI uri = resolveURI(env, "ELEPHANTSQL_URI");
return String.format("%s://%s:%s%s", "jdbc:postgresql", uri.getHost(), uri.getPort(), uri.getPath());
}
@Override
public String getUsername(Environment env) {
return isCloudFoundry(env) ? "" : resolveURI(env, "ELEPHANTSQL_URI").getUserInfo().split(":")[0];
}
@Override
public String getPassword(Environment env) {
return isCloudFoundry(env) ? "" : resolveURI(env, "ELEPHANTSQL_URI").getUserInfo().split(":")[1];
}
@Override
public String getDialect(Environment env) {
return PGSQL;
}
@Override
public String getDriveClassName(Environment env) {
return POSTGRESQL_DRIVER;
}
@Override
public boolean isHosting(Environment env) {
return ofNullable(env.getProperty("ELEPHANTSQL_URI")).isPresent() || ofNullable(env.getProperty("VCAP_SERVICES")).filter(s -> s.contains("elephantsql")).isPresent();
}
@Override
public int getMaxActive(Environment env) {
return ofNullable(env.getProperty("ELEPHANTSQL_MAX_CONNS")).map(Integer::parseInt).orElseGet(() -> super.getMaxActive(env));
}
},
/**
* See https://developers.openshift.com/en/managing-environment-variables.html
**/
OPENSHIFT {
@Override
public String getUrl(Environment env) {
String dbHost = Objects.requireNonNull(System.getenv("OPENSHIFT_POSTGRESQL_DB_HOST"), "OPENSHIFT_POSTGRESQL_DB_HOST env variable is missing");
String port = Objects.requireNonNull(System.getenv("OPENSHIFT_POSTGRESQL_DB_PORT"), "OPENSHIFT_POSTGRESQL_DB_PORT env variable is missing");
String dbName = Objects.requireNonNull(System.getenv("OPENSHIFT_APP_NAME"), "OPENSHIFT_APP_NAME env variable is missing");
return "jdbc:postgresql://" + dbHost + ":" + port + "/" + dbName;
}
@Override
public String getUsername(Environment env) {
return Objects.requireNonNull(System.getenv("OPENSHIFT_POSTGRESQL_DB_USERNAME"), "OPENSHIFT_POSTGRESQL_DB_USERNAME env variable is missing");
}
@Override
public String getPassword(Environment env) {
return Objects.requireNonNull(System.getenv("OPENSHIFT_POSTGRESQL_DB_PASSWORD"), "OPENSHIFT_POSTGRESQL_DB_PASSWORD env variable is missing");
}
@Override
public String getDialect(Environment env) {
return PGSQL;
}
@Override
public String getDriveClassName(Environment env) {
return POSTGRESQL_DRIVER;
}
@Override
public boolean isHosting(Environment env) {
return ofNullable(env.getProperty("OPENSHIFT_APP_NAME")).isPresent();
}
},
/**
* Cloud Foundry configuration.
* see https://docs.cloudfoundry.org/buildpacks/java/spring-service-bindings.html
* We assume that either the "MySql" service or "ElephantSql" have already been bound to the application.
* Anyway, since we use Spring, the Cloud Foundry engine should replace the "DataSource" bean with the right one.
*/
CLOUD_FOUNDRY {
@Override
public String getUrl(Environment env) { return "url"; }
@Override
public String getUsername(Environment env) {
return "";
}
@Override
public String getPassword(Environment env) {
return "";
}
@Override
public String getDialect(Environment env) {
return isMySql(env) ? MYSQL : PGSQL;
}
@Override
public String getDriveClassName(Environment env) {
return isMySql(env) ? MYSQL_DRIVER : POSTGRESQL_DRIVER;
}
@Override
public boolean isHosting(Environment env) {
//check if json object for services is returned
//example payload
//{
//"staging_env_json": {},
//"running_env_json": {},
//"system_env_json": {
// "VCAP_SERVICES": {
// "p-mysql": [
// {
// "name": "alfio-db",
return env.getProperty("VCAP_SERVICES") != null;
}
private boolean isMySql(Environment env) {
return ofNullable(env.getProperty("VCAP_SERVICES")).map(props -> props.contains("mysql://")).orElse(false);
}
},
HEROKU {
@Override
public String getUrl(Environment env) {
URI uri = resolveURI(env);
return String.format("%s://%s:%s%s", "jdbc:postgresql", uri.getHost(), uri.getPort(), uri.getPath());
}
@Override
public String getUsername(Environment env) {
return resolveURI(env).getUserInfo().split(":")[0];
}
@Override
public String getPassword(Environment env) {
return resolveURI(env).getUserInfo().split(":")[1];
}
@Override
public String getDialect(Environment env) {
return PGSQL;
}
@Override
public String getDriveClassName(Environment env) {
return POSTGRESQL_DRIVER;
}
@Override
public boolean isHosting(Environment env) {
return ofNullable(env.getProperty("DYNO")).isPresent();
}
private URI resolveURI(Environment env) {
return resolveURI(env, "DATABASE_URL");
}
},
DOCKER {
@Override
public String getUrl(Environment env) {
String dbHost = Objects.requireNonNull(System.getenv("DB_PORT_5432_TCP_ADDR"), "DB_PORT_5432_TCP_ADDR env variable is missing");
String port = Objects.requireNonNull(System.getenv("DB_PORT_5432_TCP_PORT"), "DB_PORT_5432_TCP_PORT env variable is missing");
String dbName = Objects.requireNonNull(System.getenv("DB_ENV_POSTGRES_DB"), "DB_ENV_POSTGRES_DB env variable is missing");
return "jdbc:postgresql://" + dbHost + ":" + port + "/" + dbName;
}
@Override
public String getUsername(Environment env) {
return Objects.requireNonNull(System.getenv("DB_ENV_POSTGRES_USERNAME"), "DB_ENV_POSTGRES_USERNAME env variable is missing");
}
@Override
public String getPassword(Environment env) {
return Objects.requireNonNull(System.getenv("DB_ENV_POSTGRES_PASSWORD"), "DB_ENV_POSTGRES_PASSWORD env variable is missing");
}
@Override
public String getDialect(Environment env) {
return PGSQL;
}
@Override
public String getDriveClassName(Environment env) {
return POSTGRESQL_DRIVER;
}
@Override
public boolean isHosting(Environment env) {
return ofNullable(env.getProperty("DB_ENV_POSTGRES_DB")).isPresent();
}
},
AWS_BEANSTALK {
@Override
public String getUrl(Environment env) {
String dbType = isMySql(env) ? "mysql" : "postgresql";
String host = env.getRequiredProperty("RDS_HOSTNAME");
String port = env.getRequiredProperty("RDS_PORT");
String db = env.getRequiredProperty("RDS_DB_NAME");
return String.format("jdbc:%s://%s:%s/%s", dbType, host, port, db);
}
@Override
public String getUsername(Environment env) {
return env.getRequiredProperty("RDS_USERNAME");
}
@Override
public String getPassword(Environment env) {
return env.getRequiredProperty("RDS_PASSWORD");
}
@Override
public String getDialect(Environment env) {
return isMySql(env) ? MYSQL : PGSQL;
}
@Override
public boolean isHosting(Environment env) {
return ofNullable(env.getProperty("RDS_HOSTNAME")).isPresent();
}
private boolean isMySql(Environment env) {
return ofNullable(env.getProperty("RDS_PORT")).filter("3306"::equals).isPresent();
}
@Override
public String getDriveClassName(Environment env) {
return isMySql(env) ? MYSQL_DRIVER : POSTGRESQL_DRIVER;
}
};
private static final String POSTGRESQL_DRIVER = "org.postgresql.Driver";
public static final String PGSQL = "PGSQL";
private static final String MYSQL_DRIVER = "com.mysql.jdbc.Driver";
public static final String MYSQL = "MYSQL";
public String getUrl(Environment env) {
return env.getRequiredProperty("datasource.url");
}
public String getUsername(Environment env) {
return env.getRequiredProperty("datasource.username");
}
public String getPassword(Environment env) {
return env.getRequiredProperty("datasource.password");
}
public String getDriveClassName(Environment env) {
return env.getRequiredProperty("datasource.driver");
}
public String getDialect(Environment env) {
return env.getRequiredProperty("datasource.dialect");
}
public int getMaxActive(Environment env) {
return ofNullable(env.getProperty("datasource.connections.max-active"))
.map(Integer::parseInt)
.orElse(10);//
}
public boolean isHosting(Environment env) {
return true;
}
static URI resolveURI(Environment env, String propertyName) {
try {
return new URI(env.getRequiredProperty(propertyName));
} catch (URISyntaxException e) {
throw new IllegalArgumentException(e);
}
}
static boolean isCloudFoundry(Environment env) {
return env.getProperty("VCAP_SERVICES") != null;
}
}