PageRenderTime 45ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/amps-maven-plugin/src/main/java/com/atlassian/maven/plugins/amps/database/Postgres.java

https://bitbucket.org/atlassian/amps
Java | 227 lines | 192 code | 17 blank | 18 comment | 27 complexity | 08bf43595588c8d3d828ef3277d442a3 MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause
  1. package com.atlassian.maven.plugins.amps.database;
  2. import com.atlassian.maven.plugins.amps.DataSource;
  3. import com.atlassian.maven.plugins.amps.product.ImportMethod;
  4. import com.google.common.annotations.VisibleForTesting;
  5. import org.apache.commons.lang3.StringUtils;
  6. import org.apache.maven.plugin.MojoExecutionException;
  7. import org.apache.maven.plugin.logging.Log;
  8. import org.codehaus.plexus.util.xml.Xpp3Dom;
  9. import javax.annotation.Nonnull;
  10. import javax.annotation.ParametersAreNonnullByDefault;
  11. import javax.annotation.Nullable;
  12. import java.sql.Driver;
  13. import java.sql.DriverManager;
  14. import java.sql.DriverPropertyInfo;
  15. import java.sql.SQLException;
  16. import java.util.Optional;
  17. import java.util.Properties;
  18. import static java.lang.String.format;
  19. import static org.twdata.maven.mojoexecutor.MojoExecutor.configuration;
  20. import static org.twdata.maven.mojoexecutor.MojoExecutor.element;
  21. import static org.twdata.maven.mojoexecutor.MojoExecutor.name;
  22. @ParametersAreNonnullByDefault
  23. public class Postgres extends AbstractDatabase {
  24. private static final String ARGUMENT = "argument";
  25. private static final String DROP_DATABASE = "DROP DATABASE IF EXISTS \"%s\";";
  26. private static final String DROP_USER = "DROP USER IF EXISTS \"%s\";";
  27. private static final String CREATE_DATABASE = "CREATE DATABASE \"%s\";";
  28. private static final String CREATE_USER = "CREATE USER \"%s\" WITH PASSWORD '%s' ;";
  29. private static final String GRANT_PERMISSION = "ALTER ROLE \"%s\" superuser; ALTER DATABASE \"%s\" OWNER TO \"%s\";";
  30. @VisibleForTesting
  31. static final String PGDBNAME = "PGDBNAME";
  32. @VisibleForTesting
  33. static final String PGHOST = "PGHOST";
  34. @VisibleForTesting
  35. static final String PGPORT = "PGPORT";
  36. public Postgres(final Log log) {
  37. super(log, true, "postgres72", "org.postgresql.Driver", "jdbc:postgresql");
  38. }
  39. @Override
  40. protected String dropDatabase(final DataSource dataSource) throws MojoExecutionException {
  41. return String.format(DROP_DATABASE, getDatabaseName(dataSource));
  42. }
  43. @Override
  44. protected String dropUser(final DataSource dataSource) {
  45. return String.format(DROP_USER, dataSource.getUsername());
  46. }
  47. @Override
  48. protected String createDatabase(final DataSource dataSource) throws MojoExecutionException {
  49. return String.format(CREATE_DATABASE, getDatabaseName(dataSource));
  50. }
  51. @Override
  52. protected String createUser(final DataSource dataSource) {
  53. return String.format(CREATE_USER, dataSource.getUsername(), dataSource.getPassword());
  54. }
  55. @Override
  56. protected String grantPermissionForUser(final DataSource dataSource) throws MojoExecutionException {
  57. return String.format(GRANT_PERMISSION,
  58. dataSource.getUsername(), getDatabaseName(dataSource), dataSource.getUsername());
  59. }
  60. @Override
  61. public Xpp3Dom getExecMavenToolImportConfiguration(final DataSource dataSource) throws MojoExecutionException {
  62. Xpp3Dom configDatabaseTool = null;
  63. if (ImportMethod.PSQL.equals(ImportMethod.getValueOf(dataSource.getImportMethod()))) {
  64. configDatabaseTool = configuration(
  65. element(name("executable"), "psql"),
  66. element(name("arguments"),
  67. element(name(ARGUMENT), "-f" + dataSource.getDumpFilePath()),
  68. element(name(ARGUMENT), "-U" + dataSource.getUsername()),
  69. element(name(ARGUMENT), "-h" + getHostName(dataSource.getUrl())),
  70. element(name(ARGUMENT), getDatabaseName(dataSource))
  71. )
  72. );
  73. }
  74. return configDatabaseTool;
  75. }
  76. private static String getHostName(final String jdbcUrl) {
  77. return Optional.ofNullable(parseURL(jdbcUrl))
  78. .map(urlProperties -> urlProperties.getProperty(PGHOST))
  79. .filter(StringUtils::isNotEmpty)
  80. .orElseThrow(() -> new IllegalArgumentException(
  81. format("Could not parse host name from Postgres URL '%s'", jdbcUrl)));
  82. }
  83. /**
  84. * reference postgres 9.3 documentation: Connecting to the Database http://jdbc.postgresql.org/documentation/93/connect.html
  85. * With JDBC, a database is represented by a URL (Uniform Resource Locator) takes one of the following forms:
  86. * jdbc:postgresql:database jdbc:postgresql://host/database jdbc:postgresql://host:port/database
  87. *
  88. * @return database name
  89. */
  90. @Override
  91. protected String getDatabaseName(final DataSource dataSource) throws MojoExecutionException {
  92. String databaseName = StringUtils.EMPTY;
  93. try {
  94. Class.forName(dataSource.getDriver());
  95. } catch (ClassNotFoundException e) {
  96. throw new MojoExecutionException("Could not load Postgresql database library from AMPS classpath");
  97. }
  98. try {
  99. final String url = dataSource.getUrl();
  100. final Driver driver = DriverManager.getDriver(url);
  101. final DriverPropertyInfo[] driverPropertyInfos = driver.getPropertyInfo(url, null);
  102. if (null != driverPropertyInfos) {
  103. for (DriverPropertyInfo driverPropertyInfo : driverPropertyInfos) {
  104. if (PGDBNAME.equals(driverPropertyInfo.name)) {
  105. databaseName = driverPropertyInfo.value;
  106. break;
  107. }
  108. }
  109. }
  110. // bug from postgresql JDBC <= 9.3
  111. if (databaseName == null) {
  112. // apply local monkey-patch
  113. final Properties driverProps = parseURL(url);
  114. if (driverProps != null) {
  115. databaseName = driverProps.getProperty(PGDBNAME);
  116. }
  117. }
  118. } catch (SQLException e) {
  119. throw new MojoExecutionException("No suitable driver");
  120. }
  121. return databaseName;
  122. }
  123. @Override
  124. @Nonnull
  125. public Xpp3Dom getSqlMavenCreateConfiguration(final DataSource dataSource) throws MojoExecutionException {
  126. final String sql = dropDatabase(dataSource) + dropUser(dataSource) + createDatabase(dataSource) +
  127. createUser(dataSource) + grantPermissionForUser(dataSource);
  128. log.info("Postgres initialization database sql: " + sql);
  129. Xpp3Dom pluginConfiguration = systemDatabaseConfiguration(dataSource);
  130. pluginConfiguration.addChild(
  131. element(name("sqlCommand"), sql).toDom()
  132. );
  133. return pluginConfiguration;
  134. }
  135. /**
  136. * Parses the given Postgres JDBC URL into its component parts.
  137. *
  138. * Copied from org.postgresql:postgresql:9.3-1102-jdbc41.jar!/org/postgresql/Driver.java
  139. *
  140. * @param url the JDBC URL to parse
  141. * @return Properties with elements added from the url, or {@code null} if the given URL can't be parsed
  142. */
  143. @Nullable
  144. @VisibleForTesting
  145. static Properties parseURL(final String url) {
  146. final Properties urlProps = new Properties();
  147. String l_urlServer = url;
  148. String l_urlArgs = "";
  149. final int l_qPos = url.indexOf('?');
  150. if (l_qPos != -1) {
  151. l_urlServer = url.substring(0, l_qPos);
  152. l_urlArgs = url.substring(l_qPos + 1);
  153. }
  154. if (!l_urlServer.startsWith("jdbc:postgresql:")) {
  155. return null;
  156. }
  157. l_urlServer = l_urlServer.substring("jdbc:postgresql:".length());
  158. if (l_urlServer.startsWith("//")) {
  159. l_urlServer = l_urlServer.substring(2);
  160. final int slash = l_urlServer.indexOf('/');
  161. if (slash == -1) {
  162. return null;
  163. }
  164. urlProps.setProperty(PGDBNAME, l_urlServer.substring(slash + 1));
  165. final String[] addresses = l_urlServer.substring(0, slash).split(",");
  166. final StringBuilder hosts = new StringBuilder();
  167. final StringBuilder ports = new StringBuilder();
  168. for (final String address : addresses) {
  169. final int portIdx = address.lastIndexOf(':');
  170. if (portIdx != -1 && address.lastIndexOf(']') < portIdx) {
  171. final String portStr = address.substring(portIdx + 1);
  172. try {
  173. Integer.parseInt(portStr);
  174. } catch (NumberFormatException ex) {
  175. return null;
  176. }
  177. ports.append(portStr);
  178. hosts.append(address.subSequence(0, portIdx));
  179. } else {
  180. ports.append("5432");
  181. hosts.append(address);
  182. }
  183. ports.append(',');
  184. hosts.append(',');
  185. }
  186. ports.setLength(ports.length() - 1);
  187. hosts.setLength(hosts.length() - 1);
  188. urlProps.setProperty(PGPORT, ports.toString());
  189. urlProps.setProperty(PGHOST, hosts.toString());
  190. } else {
  191. urlProps.setProperty(PGPORT, "5432");
  192. urlProps.setProperty(PGHOST, "localhost");
  193. urlProps.setProperty(PGDBNAME, l_urlServer);
  194. }
  195. //parse the args part of the url
  196. final String[] args = l_urlArgs.split("&");
  197. for (final String token : args) {
  198. if (token.length() == 0) {
  199. continue;
  200. }
  201. int l_pos = token.indexOf('=');
  202. if (l_pos == -1) {
  203. urlProps.setProperty(token, "");
  204. } else {
  205. urlProps.setProperty(token.substring(0, l_pos), token.substring(l_pos + 1));
  206. }
  207. }
  208. return urlProps;
  209. }
  210. }