001package io.ebean.docker.commands; 002 003import java.net.MalformedURLException; 004import java.net.URL; 005import java.nio.file.Files; 006import java.nio.file.Paths; 007import java.util.Properties; 008 009import org.slf4j.Logger; 010import org.slf4j.LoggerFactory; 011 012/** 013 * SAP HANA configuration. 014 * 015 * For more information about the HANA docker configuration see the tutorial 016 * <a href= 017 * "https://developers.sap.com/tutorials/hxe-ua-install-using-docker.html">Installing 018 * SAP HANA, express edition with Docker</a> 019 */ 020public class HanaConfig extends DbConfig { 021 022 private static final Logger log = LoggerFactory.getLogger(HanaConfig.class); 023 024 private String mountsDirectory; 025 private URL passwordsUrl; 026 private String instanceNumber; 027 private boolean agreeToSapLicense; 028 029 public HanaConfig(String version, Properties properties) { 030 this(version); 031 setProperties(properties); 032 if (!Integer.toString(this.port).matches("\\d{5}")) { 033 throw new IllegalArgumentException("Invalid port: " + this.port + ". The port must consist of exactly 5 digits."); 034 } 035 this.mountsDirectory = prop(properties, "mountsDirectory", "/data/dockermounts"); 036 if (!Files.isDirectory(Paths.get(this.mountsDirectory))) { 037 throw new IllegalArgumentException( 038 "The given mounts directory \"" + this.mountsDirectory + "\" doesn't exist or is not a directory"); 039 } 040 try { 041 this.passwordsUrl = new URL(prop(properties, "passwordsUrl", "file:///hana/mounts/passwords.json")); 042 } catch (MalformedURLException e) { 043 log.warn("Invalid passwords URL. Using default.", e); 044 try { 045 this.passwordsUrl = new URL("file:///hana/mounts/passwords.json"); 046 } catch (MalformedURLException e1) { 047 log.debug("Invalid passwords URL. Can't happen."); 048 } 049 } 050 this.instanceNumber = prop(properties, "instanceNumber", "90"); 051 if (!this.instanceNumber.matches("\\d{2}")) { 052 throw new IllegalArgumentException("Invalid instance number: " + this.instanceNumber 053 + ". The instance number must consist of exactly two digits."); 054 } 055 if (!"90".equals(this.instanceNumber)) { 056 String portStr = Integer.toString(this.port); 057 this.port = Integer.parseInt(portStr.substring(0, 1) + this.instanceNumber + portStr.substring(3)); 058 } 059 this.agreeToSapLicense = checkLicenseAgreement(properties); 060 } 061 062 public HanaConfig(String version) { 063 super("hana", 39017, 39017, version); 064 this.image = "store/saplabs/hanaexpress:" + version; 065 this.mountsDirectory = "/data/dockermounts"; 066 try { 067 this.passwordsUrl = new URL("file:///hana/mounts/passwords.json"); 068 } catch (MalformedURLException e1) { 069 log.debug("Invalid passwords URL. Can't happen."); 070 } 071 this.instanceNumber = "90"; 072 this.agreeToSapLicense = checkLicenseAgreement(); 073 setAdminUser("SYSTEM"); 074 setAdminPassword("HXEHana1"); 075 setPassword("HXEHana1"); 076 setDbName("HXE"); 077 setMaxReadyAttempts(3000); 078 } 079 080 /** 081 * Return the JDBC URL for connecting to the database 082 */ 083 public String jdbcUrl() { 084 return "jdbc:sap://localhost:" + getPort() + "/?databaseName=" + getDbName(); 085 } 086 087 /** 088 * Return the path to the container-external mounts directory that can be used 089 * by the HANA docker container to store its data. 090 * <p> 091 * The directory must be created before starting the docker container, for 092 * example, like this: 093 * 094 * <pre> 095 * sudo mkdir -p /data/<directory_name> 096 * sudo chown 12000:79 /data/<directory_name> 097 * </pre> 098 * 099 * @return The path to the external directory 100 */ 101 public String getMountsDirectory() { 102 return mountsDirectory; 103 } 104 105 /** 106 * Set the path to the container-external mounts directory that can be used by 107 * the HANA docker image to store its data. 108 * 109 * @param mountsDirectory The path to the external directory 110 */ 111 public void setMountsDirectory(String mountsDirectory) { 112 this.mountsDirectory = mountsDirectory; 113 } 114 115 /** 116 * Return the URL of the file containing the default password(s) for the HANA 117 * database users. 118 * <p> 119 * The file must contain passwords in a JSON format, for example: 120 * 121 * <pre> 122 * { 123 * "master_password" : "HXEHana1" 124 * } 125 * </pre> 126 * 127 * If the file is located in the container-external mounts directory (see 128 * {@link #getMountsDirectory()}), the URL should be 129 * {@code file:///hana/mounts/<file_name>.json} 130 * 131 * @return The URL of the file containing the default password(s) for the HANA 132 * database users. 133 */ 134 public URL getPasswordsUrl() { 135 return passwordsUrl; 136 } 137 138 /** 139 * Set the URL of the file containing the default password(s) for the HANA 140 * database users. 141 * 142 * @param passwordsUrl The URL of the file containing the default password(s) 143 * for the HANA database users. 144 */ 145 public void setPasswordsUrl(URL passwordsUrl) { 146 this.passwordsUrl = passwordsUrl; 147 } 148 149 /** 150 * Return the container-external instance number of the HANA database. 151 * 152 * A different instance number is necessary when running more than one instance 153 * of HANA on one host. The instance number can range from 00 to 99. The default 154 * instance number is 90. 155 * 156 * @return The container-external instance number of the HANA database. 157 */ 158 public String getInstanceNumber() { 159 return instanceNumber; 160 } 161 162 /** 163 * Set the container-external instance number of the HANA database. 164 * 165 * @param instanceNumber The container-external instance number of the HANA 166 * database. 167 */ 168 public void setInstanceNumber(String instanceNumber) { 169 this.instanceNumber = instanceNumber; 170 } 171 172 /** 173 * Returns whether the user agrees to the <a href= 174 * "https://www.sap.com/docs/download/cmp/2016/06/sap-hana-express-dev-agmt-and-exhibit.pdf">SAP 175 * license</a> for the HANA docker image. 176 * 177 * @return {@code true} if the user agrees to the license, {@code false} 178 * otherwise. 179 */ 180 public boolean isAgreeToSapLicense() { 181 return agreeToSapLicense; 182 } 183 184 /** 185 * Set whether the user agrees to the <a href= 186 * "https://www.sap.com/docs/download/cmp/2016/06/sap-hana-express-dev-agmt-and-exhibit.pdf">SAP 187 * license</a> for the HANA docker image. 188 * 189 * @param agreeToSapLicense Whether the user agrees to the license or not 190 */ 191 public void setAgreeToSapLicense(boolean agreeToSapLicense) { 192 this.agreeToSapLicense = agreeToSapLicense; 193 } 194 195 /** 196 * Check if the user has agreed to the <a href= 197 * "https://www.sap.com/docs/download/cmp/2016/06/sap-hana-express-dev-agmt-and-exhibit.pdf">SAP 198 * license</a> 199 * 200 * @param properties The properties to check 201 * @return {@code true} if the user has agreed to the license, {@code false} 202 * otherwise 203 */ 204 public boolean checkLicenseAgreement(Properties properties) { 205 String propertyValue = null; 206 if (properties != null) { 207 propertyValue = prop(properties, "agreeToSapLicense", null); 208 if (propertyValue != null) { 209 return Boolean.parseBoolean(propertyValue); 210 } 211 } 212 213 return checkLicenseAgreement(); 214 } 215 216 /** 217 * Check if the user has agreed to the <a href= 218 * "https://www.sap.com/docs/download/cmp/2016/06/sap-hana-express-dev-agmt-and-exhibit.pdf">SAP 219 * license</a> 220 * 221 * @return {@code true} if the user has agreed to the license, {@code false} 222 * otherwise 223 */ 224 public static boolean checkLicenseAgreement() { 225 String propertyValue = System.getProperty("hana.agreeToSapLicense"); 226 if (propertyValue != null) { 227 return Boolean.parseBoolean(propertyValue); 228 } 229 230 propertyValue = System.getenv("hana.agreeToSapLicense"); 231 if (propertyValue != null) { 232 return Boolean.parseBoolean(propertyValue); 233 } 234 235 return false; 236 } 237}