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