Java tutorial
/* * Copyright 2010 NapkinDrawing LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.napkindrawing.dbversion.task; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.io.IOUtils; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import org.apache.tools.ant.taskdefs.Property; import org.apache.tools.ant.taskdefs.SQLExec; import com.napkindrawing.dbversion.InstalledRevision; import com.napkindrawing.dbversion.Profile; import com.napkindrawing.dbversion.Revision; import com.napkindrawing.dbversion.Version; import com.napkindrawing.dbversion.loader.LoaderSpec; import com.napkindrawing.dbversion.loader.ProfileLoader; import com.napkindrawing.dbversion.loader.ProfilesLoader; import com.napkindrawing.dbversion.loader.RevisionLoader; import com.napkindrawing.dbversion.loader.RevisionsLoader; import com.napkindrawing.dbversion.loader.fs.FileSystemLoader; public abstract class DbVersionCommand extends SQLExec { private List<Profile> profiles = new ArrayList<Profile>(); private Properties config = new Properties(); private Class<? extends LoaderSpec> loaderSpecClass = FileSystemLoader.class; private ProfilesLoader profilesLoader; private ProfileLoader profileLoader; private RevisionsLoader revisionsLoader; private RevisionLoader revisionLoader; protected Boolean checkInitTables = true; protected List<InstalledRevision> installedRevisions = null; public DbVersionCommand() { super(); } private Map<Integer, String> _urlHostSchema = new HashMap<Integer, String>(); public String getHostSchema() { if (getUrl() == null || getUrl().isEmpty()) { return null; } if (_urlHostSchema.containsKey(getUrl().hashCode())) { return _urlHostSchema.get(getUrl().hashCode()); } Pattern p = Pattern.compile("^jdbc:\\w+://([\\w\\.\\-\\_\\:]+/[\\w\\.\\-\\_]+).*$"); Matcher m = p.matcher(getUrl()); if (!m.matches()) { throw new BuildException("Couldn't extract hostname and schema from jdbc url: " + getUrl()); } String hostSchema = m.group(1); _urlHostSchema.put(getUrl().hashCode(), hostSchema); return hostSchema; } protected String prependLogPrefix(String msg) { String hostSchema = getHostSchema(); if (hostSchema != null && !hostSchema.isEmpty()) { String prefix = "[" + hostSchema + "] "; if (msg.startsWith(prefix)) { return msg; } return prefix + msg; } return msg; } @Override public void log(String msg, int msgLevel) { super.log(prependLogPrefix(msg), msgLevel); } @Override public void log(String msg, Throwable t, int msgLevel) { super.log(prependLogPrefix(msg), t, msgLevel); } @Override public void log(String msg) { super.log(prependLogPrefix(msg)); } @Override public void log(Throwable t, int msgLevel) { super.log(t, msgLevel); } @Override public void init() { super.init(); LoaderSpec loaderSpec; try { loaderSpec = loaderSpecClass.newInstance(); profilesLoader = loaderSpec.getProfilesLoaderClass().newInstance(); profileLoader = loaderSpec.getProfileLoaderClass().newInstance(); revisionsLoader = loaderSpec.getRevisionsLoaderClass().newInstance(); revisionLoader = loaderSpec.getRevisionLoaderClass().newInstance(); } catch (Exception e) { throw new RuntimeException("Error initializing loaders from spec " + loaderSpecClass.getName(), e); } profilesLoader.setProfileLoader(profileLoader); profileLoader.setRevisionsLoader(revisionsLoader); revisionsLoader.setRevisionLoader(revisionLoader); } public void execute() { profilesLoader.configure(config); profileLoader.configure(config); revisionsLoader.configure(config); revisionLoader.configure(config); setProfiles(profilesLoader.loadProfiles()); summarizeProfiles(); if (checkInitTables && !canQueryRevisionTable()) { throw new BuildException("Database at " + getUrl() + " not initialized"); } if (checkInitTables) { loadInstalledRevisions(); } } private Map<String, Version> maxVersionByProfile = new HashMap<String, Version>(); public Version getMaxVersion(String profileName) { Version v = maxVersionByProfile.get(profileName); return v == null ? Version.NONE : v; } private Map<String, Profile> profilesByName = new HashMap<String, Profile>(); public Profile getProfileByName(String profileName) { if (!profilesByName.containsKey(profileName)) { throw new BuildException("No profile named '" + profileName + "'"); } return profilesByName.get(profileName); } protected void summarizeProfiles() { for (Profile profile : getProfiles()) { profilesByName.put(profile.getName(), profile); for (Revision revision : profile.getRevisions()) { String pn = profile.getName(); Version v = revision.getVersion(); if (!maxVersionByProfile.containsKey(pn)) { maxVersionByProfile.put(pn, v); profile.setMaxVersion(v); } else { if (maxVersionByProfile.get(pn).compareTo(v) < 0) { maxVersionByProfile.put(pn, v); profile.setMaxVersion(v); } } } if (profile.getMaxVersion() == null) { profile.setMaxVersion(Version.NONE); } } } private Map<String, Version> maxInstalledVersionByProfile = new HashMap<String, Version>(); public Version getMaxInstalledVersion(String profileName) { Version v = maxInstalledVersionByProfile.get(profileName); return v == null ? Version.NONE : v; } protected void summarizeInstalledRevisions() { for (InstalledRevision i : installedRevisions) { String pn = i.getProfileName(); Version v = i.getVersion(); getProfileByName(pn).getInstalledRevisions().add(i); if (!installedProfileNames.contains(pn)) { installedProfileNames.add(pn); } if (lastInstalledRevision == null || lastInstalledRevision.getUpgradeDate().compareTo(i.getUpgradeDate()) < 0) { lastInstalledRevision = i; } if (!maxInstalledVersionByProfile.containsKey(pn)) { maxInstalledVersionByProfile.put(pn, v); getProfileByName(pn).setMaxInstalledVersion(v); } else { if (maxInstalledVersionByProfile.get(pn).compareTo(v) < 0) { maxInstalledVersionByProfile.put(pn, v); getProfileByName(pn).setMaxInstalledVersion(v); } } } for (Profile p : getProfiles()) { if (p.getMaxInstalledVersion() == null) { p.setMaxInstalledVersion(Version.NONE); } } } protected InstalledRevision lastInstalledRevision = null; public InstalledRevision getLastInstalledRevision() { if (installedRevisions == null) { loadInstalledRevisions(); } return lastInstalledRevision; } protected void loadInstalledRevisions() { installedRevisions = new ArrayList<InstalledRevision>(); Connection conn = getConnection(); Statement stmt = null; try { stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM __database_revision"); installedRevisions = new ArrayList<InstalledRevision>(); while (rs.next()) { installedRevisions.add(new InstalledRevision(rs)); } log(installedRevisions.size() + " Revisions Read", Project.MSG_DEBUG); } catch (SQLException e) { throw new RuntimeException("Error querying database", e); } finally { if (stmt != null) { try { stmt.close(); } catch (SQLException ignore) { } } } summarizeInstalledRevisions(); } protected List<String> installedProfileNames = new ArrayList<String>(); public List<String> getInstalledProfileNames() { if (installedProfileNames == null) { loadInstalledRevisions(); } return installedProfileNames; } public List<InstalledRevision> getInstalledRevisions() { if (installedRevisions == null) { loadInstalledRevisions(); } return installedRevisions; } public boolean canQueryRevisionTable() { Connection conn = getConnection(); Statement stmt = null; try { stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery( "SELECT count(*), '" + Thread.currentThread() + "' AS thread FROM __database_revision"); if (rs.first()) { return true; } } catch (SQLException e) { if (e.getMessage().matches("^Table.*doesn't exist.*$")) { // MySQL } else { throw new RuntimeException("Error querying database", e); } } finally { if (stmt != null) { try { stmt.close(); } catch (SQLException ignore) { } } } return false; } public List<Profile> getProfiles() { return profiles; } public void setProfiles(List<Profile> profiles) { this.profiles = profiles; } public void addConfiguredLoaderProperty(Property property) { log("Adding loaderProperty: " + property.getName() + " => " + property.getValue(), Project.MSG_DEBUG); config.put(property.getName(), property.getValue()); } public void setLoaderSpecClass(Class<? extends LoaderSpec> loaderSpecClass) { this.loaderSpecClass = loaderSpecClass; } protected String loadResourceFile(String path) { log("Attempting to load resource file: " + path); ClassLoader loader = Thread.currentThread().getContextClassLoader(); log("Loader: " + loader); InputStream createInputStream = loader.getResourceAsStream(path); if (createInputStream == null) { throw new RuntimeException("Couldnt' load resource file: " + path); } String contents; try { contents = IOUtils.toString(createInputStream); } catch (IOException e1) { throw new RuntimeException(e1); } return contents; } protected void closeQuietly() { try { if (getStatement() != null) { getStatement().close(); } } catch (SQLException ex) { // ignore } try { if (getConnection() != null) { getConnection().close(); } } catch (SQLException ex) { // ignore } } public Boolean getCheckInitTables() { return checkInitTables; } public void setCheckInitTables(Boolean checkInitTables) { this.checkInitTables = checkInitTables; } }