org.bitbucket.bradleysmithllc.etlunit.feature.database.DatabaseFeatureModule Maven / Gradle / Ivy
package org.bitbucket.bradleysmithllc.etlunit.feature.database;
import com.google.gson.stream.JsonWriter;
import com.google.inject.Binder;
import com.google.inject.Injector;
import com.google.inject.Module;
import org.apache.commons.io.FileUtils;
import org.bitbucket.bradleysmithllc.etlunit.*;
import com.fasterxml.jackson.databind.JsonNode;
import org.bitbucket.bradleysmithllc.etlunit.feature.AbstractFeature;
import org.bitbucket.bradleysmithllc.etlunit.feature.FeatureModule;
import org.bitbucket.bradleysmithllc.etlunit.feature.database.db.Database;
import org.bitbucket.bradleysmithllc.etlunit.feature.database.util.DimensionalHashMap;
import org.bitbucket.bradleysmithllc.etlunit.feature.database.util.DimensionalMap;
import org.bitbucket.bradleysmithllc.etlunit.io.FileBuilder;
import org.bitbucket.bradleysmithllc.etlunit.util.ObjectUtils;
import org.bitbucket.bradleysmithllc.json.validator.JsonUtils;
import org.bitbucket.bradleysmithllc.etlunit.parser.*;
import org.bitbucket.bradleysmithllc.etlunit.util.IOUtils;
import org.bitbucket.bradleysmithllc.etlunit.util.VelocityUtil;
import javax.inject.Inject;
import java.io.*;
import java.net.URL;
import java.util.*;
@FeatureModule
public class DatabaseFeatureModule extends AbstractFeature {
private RuntimeSupport runtimeSupport;
private DatabaseRuntimeSupport databaseRuntimeSupport;
private static final List prerequisites = Arrays.asList("logging", "file");
private final Map implementationMap = new HashMap();
protected final DimensionalMap initializedDbs = new DimensionalHashMap();
private DatabaseConfiguration databaseConfiguration;
private DatabaseClassListener classListener;
private JDBCClientImpl jdbcClient;
public void addDatabaseImplementation(DatabaseImplementation impl) {
if (implementationMap.containsKey(impl.getImplementationId())) {
throw new IllegalArgumentException("Database Implementation " + impl.getImplementationId() + " already installed");
}
implementationMap.put(impl.getImplementationId(), postCreate(impl));
}
@Inject
public void setRuntimeSupport(RuntimeSupport runtimeSupport) {
this.runtimeSupport = runtimeSupport;
}
@Override
protected Injector preCreateSub(Injector inj) {
this.databaseRuntimeSupport = postCreate(new DatabaseRuntimeSupportImpl());
jdbcClient = postCreate(new JDBCClientImpl());
Injector childInjector = inj.createChildInjector(new Module() {
public void configure(Binder binder) {
binder.bind(DatabaseRuntimeSupport.class).toInstance(databaseRuntimeSupport);
binder.bind(JDBCClient.class).toInstance(jdbcClient);
}
});
return childInjector;
}
@Override
public void initialize(Injector inj) {
if (featureConfiguration != null) {
databaseConfiguration = new DatabaseConfiguration(
featureConfiguration,
runtimeSupport.getProjectName(),
runtimeSupport.getProjectVersion(),
runtimeSupport.getProjectUser(),
runtimeSupport.getProjectUID()
);
DbAssertExtender object1 = new DbAssertExtender(this);
inj.injectMembers(object1);
DbOperationsExecutor object = new DbOperationsExecutor(this);
inj.injectMembers(object);
classListener = new DatabaseClassListener(this, databaseConfiguration, object1, object);
inj.injectMembers(classListener);
}
}
@Override
public List getPrerequisites() {
return prerequisites;
}
public String getFeatureName() {
return "database";
}
@Override
public ClassListener getListener() {
return classListener;
}
/*
private SQLAggregator resolveSQLRef(String ddlRef, DatabaseConnection databaseConnection, SQLAggregator.SQLLocator locator) throws IOException, TestExecutionError {
String text = locator.locate(ddlRef, databaseConnection);
SQLAggregatorImpl ddlAgg = new SQLAggregatorImpl(ddlRef, locator, databaseConnection);
return ddlAgg;
}
*/
public SQLAggregator resolveSQLRef(String text) throws IOException, TestExecutionError {
SQLAggregatorImpl ddlAgg = new SQLAggregatorImpl(text);
return ddlAgg;
}
public SQLAggregator resolveDDLRef(final DDLSourceRef ddlRef, DatabaseConnection databaseConnection) throws IOException, TestExecutionError {
String rawDDL = SQLAggregatorImpl.makeText(ddlRef, new SQLAggregator.SQLLocator() {
public String locate(DDLSourceRef ref, DatabaseConnection connection) throws IOException, TestExecutionError {
return getDDLText(ref).getText();
}
}, databaseConnection);
File output = new FileBuilder(databaseRuntimeSupport.getGeneratedSourceDirectory(databaseConnection, null)).name("GENERATED_" + ddlRef.urlPath().replace('/', '_')).file();
IOUtils.writeBufferToFile(output, new StringBuffer(rawDDL));
try {
Map bean = new HashMap();
bean.put("databaseConnection", databaseConnection);
bean.put("databaseRuntimeSupport", databaseRuntimeSupport);
bean.put("databaseConfiguration", databaseConfiguration);
bean.put("runtimeSupport", runtimeSupport);
String newDDL = VelocityUtil.writeTemplate(rawDDL, bean);
SQLAggregatorImpl impl = new SQLAggregatorImpl(newDDL);
File outputProcessed = new FileBuilder(databaseRuntimeSupport.getGeneratedSourceDirectory(databaseConnection, null)).name("PROCESSED_" + ddlRef.urlPath().replace('/', '_')).file();
IOUtils.writeBufferToFile(outputProcessed, new StringBuffer(newDDL));
File outputClean = new FileBuilder(databaseRuntimeSupport.getGeneratedSourceDirectory(databaseConnection, null)).name("CLEAN_" + ddlRef.urlPath().replace('/', '_')).file();
IOUtils.writeBufferToFile(outputClean, new StringBuffer(impl.getCleanText()));
return impl;
} catch (Exception e) {
throw new IOException(e);
}
}
/**
* Lookup the ddl text using a search path and return.
*
* @param ddlRef
* @return
* @throws IOException
* @throws TestExecutionError
*/
protected DDLSourceRef getDDLText(final DDLSourceRef ddlRef) throws IOException, TestExecutionError {
// Enhance this to look at the classpath if the file isn't found
DDLSourceRef actualRef = ddlRef;
if (!loadDDLRef(actualRef)) {
applicationLog.info("Unable to find ddl for reference: " + actualRef.urlPath() + ", trying without the connection ID");
// try one more time, this time without the connection id
actualRef = new DDLSourceRef(actualRef.getImplementationId(), null, actualRef.getSource());
if (!loadDDLRef(actualRef)) {
// try one more time, this time without the implementation id
actualRef = new DDLSourceRef(null, null, actualRef.getSource());
if (!loadDDLRef(actualRef)) {
throw new TestExecutionError("Error resolving ddl ref: " + actualRef.urlPath(), DatabaseConstants.ERR_MISSSING_DDL);
}
}
}
// instrument the ddl text with ref tags and line numbers
StringWriter stringWriter = new StringWriter();
PrintWriter str = new PrintWriter(stringWriter);
BufferedReader br = new BufferedReader(new StringReader(actualRef.getText()));
String line;
int lineNo = 1;
while ((line = br.readLine()) != null) {
str.print("/**\tDDL Source [" + actualRef.refPath() + ":" + lineNo++ + "]\t**/");
str.println(line);
}
actualRef.setText(stringWriter.toString());
return actualRef;
}
private boolean loadDDLRef(DDLSourceRef ddl) throws IOException {
String implementationId = ddl.getImplementationId();
String databaseId = ddl.getDatabaseId();
if (implementationId != null && !ddl.isAbsolute()) {
FileBuilder ddlFile = new FileBuilder(runtimeSupport.getFeatureSourceDirectory(implementationId));
if (databaseId != null) {
ddlFile.subdir(databaseId);
}
File f = ddlFile.mkdirs().name(ddl.getSource()).file();
if (f.exists()) {
ddl.setText(FileUtils.readFileToString(f));
return true;
}
}
// check for a classpath reference
URL ref = ObjectUtils.firstNotNull(Thread.currentThread().getContextClassLoader(), getClass().getClassLoader()).getResource(ddl.urlPath());
if (ref != null) {
ddl.setText(IOUtils.readURLToString(ref));
return true;
}
return false;
}
protected void updateDatabaseFlag(String mode, DatabaseConnection databaseConnection) {
JsonNode configNode = databaseConnection.getRawDatabaseConfiguration();
File flagFile = getFlagFile(mode, databaseConnection);
try {
IOUtils.writeBufferToFile(flagFile, new StringBuffer(configNode.toString()));
for (String script : databaseConnection.getSchemaScripts()) {
File ref = getDDLFlagFile(script, mode, databaseConnection);
StringBuilder bui = new StringBuilder();
DDLSourceRef ddlSourceRef = new DDLSourceRef(databaseConnection.getImplementationId(), databaseConnection.getId(), script);
SQLAggregator SQLAggregator = resolveDDLRef(ddlSourceRef, databaseConnection);
bui.append(SQLAggregator.getText());
long crc = IOUtils.checkSumStream(new ByteArrayInputStream(bui.toString().getBytes())).getValue();
IOUtils.writeBufferToFile(ref, new StringBuffer("{\"checksum\": " + crc + "}"));
}
} catch (Exception e) {
throw new IllegalArgumentException("", e);
}
}
private File getDDLFlagFile(String ref, String mode, DatabaseConnection databaseConnection) {
return runtimeSupport.createGeneratedSourceFile(databaseConnection.getImplementationId(), "ddl." + ref + "." + databaseConnection.getId() + "." + mode + "." + runtimeSupport.getProjectUID() + ".chk");
}
private File getFlagFile(String mode, DatabaseConnection databaseConnection) {
return runtimeSupport.createGeneratedSourceFile(databaseConnection.getImplementationId(), "database." + databaseConnection.getId() + "." + mode + "." + runtimeSupport.getProjectUID() + ".tag");
}
protected boolean databaseOutOfDate(String mode, DatabaseConnection databaseConnection) {
File flagFile = getFlagFile(mode, databaseConnection);
JsonNode configNode = databaseConnection.getRawDatabaseConfiguration();
try {
if (flagFile.exists()) {
JsonNode flgNode = JsonUtils.loadJson(flagFile);
if (flgNode.equals(configNode)) {
for (String script : databaseConnection.getSchemaScripts()) {
File ref = getDDLFlagFile(script, mode, databaseConnection);
if (!ref.exists()) {
return true;
}
StringBuilder bui = new StringBuilder();
DDLSourceRef ddlSourceRef = new DDLSourceRef(databaseConnection.getImplementationId(), databaseConnection.getId(), script);
SQLAggregator SQLAggregator = resolveDDLRef(ddlSourceRef, databaseConnection);
bui.append(SQLAggregator.getText());
long crc = IOUtils.checkSumStream(new ByteArrayInputStream(bui.toString().getBytes())).getValue();
JsonNode node = JsonUtils.loadJson(ref);
if (node.get("checksum").asLong() != crc) {
return true;
}
}
return false;
}
}
} catch (Exception e) {
throw new IllegalArgumentException("", e);
}
return true;
}
protected void propogateProperties(ETLTestValueObjectBuilder builder, DatabaseConnection databaseConnection, String mode) {
builder.key("database-name").value(databaseConnection.getDatabaseName(mode));
builder.key("login-name").value(databaseConnection.getLoginName(mode));
builder.key("password").value(databaseConnection.getPassword(mode));
// pass on the default server name if none specified
builder.key("server-name").value(databaseConnection.getServerName());
// pass a jdbc url along for external integration
DatabaseImplementation impl = getImplementation(databaseConnection.getId());
builder.key("jdbc-url").value(impl.getJdbcUrl(databaseConnection, mode));
builder.key("jdbc-driver").value(impl.getJdbcDriverClass().getName());
}
DatabaseImplementation getImplementation(String connectionId) {
DatabaseConnection conn = databaseConfiguration.getDatabaseConnection(connectionId);
return implementationMap.get(conn.getImplementationId());
}
public DatabaseConfiguration getDatabaseConfiguration() {
return databaseConfiguration;
}
@Override
public void dispose() {
for (Map.Entry entry : implementationMap.entrySet()) {
entry.getValue().dispose();
}
}
@Override
protected List getSupportedFolderNamesSub() {
return Arrays.asList("data", "sql");
}
@Override
public ClassDirector getDirector() {
return new NullClassDirector() {
@Override
public void endBroadcast() {
for (DatabaseConnection dc : databaseConfiguration.getConnectionMap().values()) {
if (dc.hasDatabase() && !dc.getDatabase().isClean()) {
Database db = dc.getDatabase();
// any database meta data which has been loaded must be persisted
File databaseMetadataTarget = new FileBuilder(databaseRuntimeSupport.getGeneratedSourceDirectory(dc, null)).name(dc.getId() + "_metadata.json").file();
try {
FileWriter fw = new FileWriter(databaseMetadataTarget);
try {
JsonWriter jw = new JsonWriter(new BufferedWriter(fw));
jw.beginObject();
db.toJson(jw);
jw.endObject();
jw.close();
} finally {
fw.close();
}
} catch (IOException e) {
throw new RuntimeException("Error persisting database meta data", e);
}
}
}
}
};
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy