package pt.ist.fenixframework.pstm.repository; import java.io.File; import java.io.IOException; import java.net.URL; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.apache.commons.io.FileUtils; import org.apache.ojb.broker.PersistenceBroker; import org.apache.ojb.broker.PersistenceBrokerFactory; import org.apache.ojb.broker.accesslayer.LookupException; import org.apache.ojb.broker.metadata.ClassDescriptor; import org.apache.ojb.broker.metadata.CollectionDescriptor; import org.joda.time.DateTime; import pt.ist.fenixframework.FenixFramework; import pt.ist.fenixframework.backend.fenixjvstm.FenixJvstmConfig; import pt.ist.fenixframework.dml.DomainModel; import pt.ist.fenixframework.dml.DomainRelation; import pt.ist.fenixframework.dml.Role; import pt.ist.fenixframework.pstm.repository.database.DatabaseDescriptorFactory; public class SQLUpdateGenerator { public static String generateSqlUpdates(final URL[] modelUrls, final String db, final String dbUser, final String dbPass, String charset, boolean genDrops) throws SQLException, LookupException { FenixFramework.initialize(new FenixJvstmConfig() { { domainModelURLs = modelUrls; dbAlias = db; dbUsername = dbUser; dbPassword = dbPass; } }); final PersistenceBroker persistenceBroker = PersistenceBrokerFactory.defaultPersistenceBroker(); Connection connection = persistenceBroker.serviceConnectionManager().getConnection(); return generateSqlUpdates(FenixFramework.getDomainModel(), connection, charset, genDrops); } public static String generateSqlUpdates(DomainModel model, Connection connection, String charset, boolean genDrops) throws SQLException { Set<String> existingTables = getExistingTables(connection); Map<String, SQLTableChangeSet> changes = new HashMap<String, SQLTableChangeSet>(); for (ClassDescriptor clazz : DatabaseDescriptorFactory.getDescriptorTable().values()) { String tablename = clazz.getFullTableName(); if (!tablename.startsWith("OJB")) { if (!changes.containsKey(tablename)) { existingTables.remove(tablename); changes.put(tablename, new SQLTableChangeSet(new SQLTableInfo(tablename, connection))); } SQLTableChangeSet change = changes.get(tablename); change.addClassDescriptor(clazz); } for (final Iterator iterator = clazz.getCollectionDescriptors().iterator(); iterator.hasNext();) { final CollectionDescriptor collectionDescriptor = (CollectionDescriptor) iterator.next(); final String indirectionTablename = collectionDescriptor.getIndirectionTable(); if (indirectionTablename != null) { if (!changes.containsKey(indirectionTablename)) { existingTables.remove(indirectionTablename); changes.put(indirectionTablename, new SQLTableChangeSet( new SQLTableInfo(indirectionTablename, connection))); } SQLTableChangeSet change = changes.get(indirectionTablename); change.addCollectionDescriptor(collectionDescriptor); } } } for (DomainRelation relation : model.getDomainRelations()) { if (is1toNRelation(relation)) { { ClassDescriptor clazz = getOtherRoleClassDescriptor(relation.getFirstRole()); SQLTableChangeSet change = changes.get(clazz.getFullTableName()); change.addIndex(relation.getFirstRole()); } { ClassDescriptor clazz = getOtherRoleClassDescriptor(relation.getSecondRole()); SQLTableChangeSet change = changes.get(clazz.getFullTableName()); change.addIndex(relation.getSecondRole()); } } } StringBuilder updates = new StringBuilder(); if (genDrops) { for (String table : existingTables) { if (!table.startsWith("OJB_") && !table.startsWith("FF$")) { updates.append("drop table " + table + ";\n"); } } } for (SQLTableChangeSet change : changes.values()) { updates.append(change.generateSqlUpdates(genDrops, charset)); } return updates.toString(); } private static Set<String> getExistingTables(Connection connection) throws SQLException { Statement statement = null; ResultSet resultSet = null; Set<String> existing = new HashSet<String>(); try { statement = connection.createStatement(); resultSet = statement.executeQuery("show tables"); while (resultSet.next()) { existing.add(resultSet.getString(1)); } return existing; } finally { if (resultSet != null) { resultSet.close(); } if (statement != null) { statement.close(); } } } private static ClassDescriptor getOtherRoleClassDescriptor(Role role) { String classname = role.getOtherRole().getType().getFullName(); return DatabaseDescriptorFactory.getDescriptorTable().get(classname); } private static boolean is1toNRelation(DomainRelation domRelation) { int multiplicity1 = domRelation.getFirstRole().getMultiplicityUpper(); int multiplicity2 = domRelation.getSecondRole().getMultiplicityUpper(); return ((multiplicity1 == 1) && (multiplicity2 != 1)) || ((multiplicity1 != 1) && (multiplicity2 == 1)); } private static String getArg(String[] args, int index) { if (args.length < index) { System.out.println("Usage: SQLUpdateGenerator <dbAlias> <dbUser> <dbPasswd> <dmlFile>+"); System.exit(1); } return args[index]; } public static void main(String[] args) throws IOException, LookupException, SQLException { int nextArg = 0; String tableCharset = null; boolean genDrops = false; while (nextArg < args.length && args[nextArg].startsWith("-")) { if (nextArg < args.length && args[nextArg].equals("-charset")) { nextArg++; tableCharset = getArg(args, nextArg++); } else if (nextArg < args.length && args[nextArg].equals("-genDrops")) { nextArg++; genDrops = true; } } final String dbAliasArg = getArg(args, nextArg++); final String dbUserArg = getArg(args, nextArg++); final String dbPassArg = getArg(args, nextArg++); final String plugins = getArg(args, nextArg++); // all the remaining args are DML files final String[] domainModelFiles = Arrays.copyOfRange(args, nextArg, args.length); String updates = generateSqlUpdates(domainModelFiles, dbAliasArg, dbUserArg, dbPassArg, tableCharset, genDrops); final File file = new File("etc/database_operations/updates.sql"); if (file.exists()) { updates = FileUtils.readFileToString(file) + "\n\n\n" + "-- Inserted at " + new DateTime() + "\n\n" + updates; } FileUtils.writeStringToFile(file, updates); } }