001package com.avaje.ebean.dbmigration; 002 003import com.avaje.ebean.Transaction; 004import com.avaje.ebean.config.ServerConfig; 005import com.avaje.ebean.dbmigration.model.CurrentModel; 006import com.avaje.ebeaninternal.api.SpiEbeanServer; 007import com.avaje.ebeaninternal.extraddl.model.ExtraDdlXmlReader; 008import org.avaje.dbmigration.ddl.DdlRunner; 009 010import javax.persistence.PersistenceException; 011import java.io.File; 012import java.io.FileReader; 013import java.io.FileWriter; 014import java.io.IOException; 015import java.io.InputStream; 016import java.io.InputStreamReader; 017import java.io.LineNumberReader; 018import java.io.Reader; 019import java.sql.Connection; 020import java.sql.SQLException; 021 022/** 023 * Controls the generation and execution of "Create All" and "Drop All" DDL scripts. 024 * 025 * Typically the "Create All" DDL is executed for running tests etc and has nothing to do 026 * with DB Migration (diff based) DDL. 027 */ 028public class DdlGenerator { 029 030 private final SpiEbeanServer server; 031 032 private final boolean generateDdl; 033 private final boolean runDdl; 034 private final boolean createOnly; 035 036 private CurrentModel currentModel; 037 private String dropAllContent; 038 private String createAllContent; 039 040 public DdlGenerator(SpiEbeanServer server, ServerConfig serverConfig) { 041 this.server = server; 042 this.generateDdl = serverConfig.isDdlGenerate(); 043 this.runDdl = serverConfig.isDdlRun(); 044 this.createOnly = serverConfig.isDdlCreateOnly(); 045 } 046 047 /** 048 * Generate the DDL and then run the DDL based on property settings 049 * (ebean.ddl.generate and ebean.ddl.run etc). 050 */ 051 public void execute(boolean online) { 052 generateDdl(); 053 if (online) { 054 runDdl(); 055 } 056 } 057 058 /** 059 * Generate the DDL drop and create scripts if the properties have been set. 060 */ 061 protected void generateDdl() { 062 if (generateDdl) { 063 if (!createOnly) { 064 writeDrop(getDropFileName()); 065 } 066 writeCreate(getCreateFileName()); 067 } 068 } 069 070 /** 071 * Run the DDL drop and DDL create scripts if properties have been set. 072 */ 073 protected void runDdl() { 074 075 if (runDdl) { 076 try { 077 runInitSql(); 078 runDropSql(); 079 runCreateSql(); 080 runSeedSql(); 081 082 } catch (IOException e) { 083 String msg = "Error reading drop/create script from file system"; 084 throw new RuntimeException(msg, e); 085 } 086 } 087 } 088 089 /** 090 * Execute all the DDL statements in the script. 091 */ 092 public int runScript(boolean expectErrors, String content, String scriptName) { 093 094 DdlRunner runner = new DdlRunner(expectErrors, scriptName); 095 096 Transaction transaction = server.createTransaction(); 097 Connection connection = transaction.getConnection(); 098 try { 099 if (expectErrors) { 100 connection.setAutoCommit(true); 101 } 102 int count = runner.runAll(content, connection); 103 if (expectErrors) { 104 connection.setAutoCommit(false); 105 } 106 transaction.commit(); 107 return count; 108 109 } catch (SQLException e) { 110 throw new PersistenceException("Failed to run script", e); 111 112 } finally { 113 transaction.end(); 114 } 115 } 116 117 protected void runDropSql() throws IOException { 118 if (!createOnly) { 119 if (dropAllContent == null) { 120 dropAllContent = readFile(getDropFileName()); 121 } 122 runScript(true, dropAllContent, getDropFileName()); 123 } 124 } 125 126 protected void runCreateSql() throws IOException { 127 if (createAllContent == null) { 128 createAllContent = readFile(getCreateFileName()); 129 } 130 runScript(false, createAllContent, getCreateFileName()); 131 132 String ignoreExtraDdl = System.getProperty("ebean.ignoreExtraDdl"); 133 if (!"true".equalsIgnoreCase(ignoreExtraDdl)) { 134 String extraApply = ExtraDdlXmlReader.buildExtra(server.getDatabasePlatform().getName()); 135 if (extraApply != null) { 136 runScript(false, extraApply, "extra-dll"); 137 } 138 } 139 } 140 141 protected void runInitSql() throws IOException { 142 runResourceScript(server.getServerConfig().getDdlInitSql()); 143 } 144 145 protected void runSeedSql() throws IOException { 146 runResourceScript(server.getServerConfig().getDdlSeedSql()); 147 } 148 149 protected void runResourceScript(String sqlScript) throws IOException { 150 151 if (sqlScript != null) { 152 InputStream is = getClassLoader().getResourceAsStream(sqlScript); 153 if (is != null) { 154 String content = readContent(new InputStreamReader(is)); 155 runScript(false, content, sqlScript); 156 } 157 } 158 } 159 160 /** 161 * Return the classLoader to use to read sql scripts as resources. 162 */ 163 protected ClassLoader getClassLoader() { 164 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 165 if (cl == null) { 166 cl = this.getClassLoader(); 167 } 168 return cl; 169 } 170 171 protected void writeDrop(String dropFile) { 172 173 try { 174 writeFile(dropFile, generateDropAllDdl()); 175 } catch (IOException e) { 176 throw new PersistenceException("Error generating Drop DDL", e); 177 } 178 } 179 180 protected void writeCreate(String createFile) { 181 182 try { 183 writeFile(createFile, generateCreateAllDdl()); 184 } catch (IOException e) { 185 throw new PersistenceException("Error generating Create DDL", e); 186 } 187 } 188 189 protected String generateDropAllDdl() { 190 191 try { 192 dropAllContent = currentModel().getDropAllDdl(); 193 return dropAllContent; 194 } catch (IOException e) { 195 throw new RuntimeException(e); 196 } 197 } 198 199 protected String generateCreateAllDdl() { 200 201 try { 202 createAllContent = currentModel().getCreateDdl(); 203 return createAllContent; 204 } catch (IOException e) { 205 throw new RuntimeException(e); 206 } 207 } 208 209 protected String getDropFileName() { 210 return server.getName() + "-drop-all.sql"; 211 } 212 213 protected String getCreateFileName() { 214 return server.getName() + "-create-all.sql"; 215 } 216 217 protected CurrentModel currentModel() { 218 if (currentModel == null) { 219 currentModel = new CurrentModel(server); 220 } 221 return currentModel; 222 } 223 224 protected void writeFile(String fileName, String fileContent) throws IOException { 225 226 File f = new File(fileName); 227 228 FileWriter fw = new FileWriter(f); 229 try { 230 fw.write(fileContent); 231 fw.flush(); 232 } finally { 233 fw.close(); 234 } 235 } 236 237 protected String readFile(String fileName) throws IOException { 238 239 File f = new File(fileName); 240 if (!f.exists()) { 241 return null; 242 } 243 244 return readContent(new FileReader(f)); 245 } 246 247 protected String readContent(Reader reader) throws IOException { 248 249 StringBuilder buf = new StringBuilder(); 250 251 LineNumberReader lineReader = new LineNumberReader(reader); 252 try { 253 String s; 254 while ((s = lineReader.readLine()) != null) { 255 buf.append(s).append("\n"); 256 } 257 return buf.toString(); 258 259 } finally { 260 lineReader.close(); 261 } 262 } 263 264}