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}