Java tutorial
/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.portal.kernel.upgrade; import com.liferay.petra.string.StringBundler; import com.liferay.petra.string.StringPool; import com.liferay.portal.kernel.dao.db.BaseDBProcess; import com.liferay.portal.kernel.dao.db.DB; import com.liferay.portal.kernel.dao.db.DBInspector; import com.liferay.portal.kernel.dao.db.DBManagerUtil; import com.liferay.portal.kernel.dao.db.DBProcessContext; import com.liferay.portal.kernel.dao.db.IndexMetadata; import com.liferay.portal.kernel.dao.db.IndexMetadataFactoryUtil; import com.liferay.portal.kernel.dao.jdbc.DataAccess; import com.liferay.portal.kernel.io.unsync.UnsyncBufferedReader; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.upgrade.util.UpgradeColumn; import com.liferay.portal.kernel.upgrade.util.UpgradeTable; import com.liferay.portal.kernel.upgrade.util.UpgradeTableFactoryUtil; import com.liferay.portal.kernel.util.ClassUtil; import com.liferay.portal.kernel.util.LoggingTimer; import com.liferay.portal.kernel.util.ObjectValuePair; import com.liferay.portal.kernel.util.PortalClassLoaderUtil; import com.liferay.portal.kernel.util.StringUtil; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.lang.reflect.Field; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * @author Brian Wing Shun Chan * @author Alexander Chow */ public abstract class UpgradeProcess extends BaseDBProcess implements UpgradeStep { public void clearIndexesCache() { _portalIndexesSQL.clear(); } public int getThreshold() { // This upgrade process will only run if the build number is larger than // the returned threshold value. Return 0 to always run this upgrade // process. return 0; } public void upgrade() throws UpgradeException { long start = System.currentTimeMillis(); String message = "Completed upgrade process "; try (Connection con = DataAccess.getConnection()) { connection = con; if (isSkipUpgradeProcess()) { return; } if (_log.isInfoEnabled()) { _log.info("Upgrading " + ClassUtil.getClassName(this)); } doUpgrade(); } catch (Throwable t) { message = "Failed upgrade process "; throw new UpgradeException(t); } finally { connection = null; if (_log.isInfoEnabled()) { _log.info(StringBundler.concat(message, ClassUtil.getClassName(this), " in ", System.currentTimeMillis() - start, " ms")); } } } @Override public void upgrade(DBProcessContext dbProcessContext) throws UpgradeException { upgrade(); } public void upgrade(UpgradeProcess upgradeProcess) throws UpgradeException { upgradeProcess.upgrade(); } public interface Alterable { public static boolean containsIgnoreCase(Collection<String> columnNames, String columnName) { for (String curColumnName : columnNames) { if (StringUtil.equalsIgnoreCase(curColumnName, columnName)) { return true; } } return false; } public String getSQL(String tableName); public boolean shouldAddIndex(Collection<String> columnNames); public boolean shouldDropIndex(Collection<String> columnNames); } public class AlterColumnName implements Alterable { public AlterColumnName(String oldColumnName, String newColumn) { _oldColumnName = oldColumnName; _newColumn = newColumn; String newColumnName = StringUtil.extractFirst(newColumn, StringPool.SPACE); if (newColumnName != null) { _newColumnName = newColumnName; } else { _newColumnName = _newColumn; } } @Override public String getSQL(String tableName) { StringBundler sb = new StringBundler(6); sb.append("alter_column_name "); sb.append(tableName); sb.append(StringPool.SPACE); sb.append(_oldColumnName); sb.append(StringPool.SPACE); sb.append(_newColumn); return sb.toString(); } @Override public boolean shouldAddIndex(Collection<String> columnNames) { return Alterable.containsIgnoreCase(columnNames, _newColumnName); } @Override public boolean shouldDropIndex(Collection<String> columnNames) { return Alterable.containsIgnoreCase(columnNames, _oldColumnName); } private final String _newColumn; private final String _newColumnName; private final String _oldColumnName; } public class AlterColumnType implements Alterable { public AlterColumnType(String columnName, String newType) { _columnName = columnName; _newType = newType; } @Override public String getSQL(String tableName) { StringBundler sb = new StringBundler(6); sb.append("alter_column_type "); sb.append(tableName); sb.append(StringPool.SPACE); sb.append(_columnName); sb.append(StringPool.SPACE); sb.append(_newType); return sb.toString(); } @Override public boolean shouldAddIndex(Collection<String> columnNames) { return Alterable.containsIgnoreCase(columnNames, _columnName); } @Override public boolean shouldDropIndex(Collection<String> columnNames) { return Alterable.containsIgnoreCase(columnNames, _columnName); } private final String _columnName; private final String _newType; } public class AlterTableAddColumn implements Alterable { public AlterTableAddColumn(String columnName) { _columnName = columnName; } @Override public String getSQL(String tableName) { StringBundler sb = new StringBundler(4); sb.append("alter table "); sb.append(tableName); sb.append(" add "); sb.append(_columnName); return sb.toString(); } @Override public boolean shouldAddIndex(Collection<String> columnNames) { return Alterable.containsIgnoreCase(columnNames, _columnName); } @Override public boolean shouldDropIndex(Collection<String> columnNames) { return false; } private final String _columnName; } public class AlterTableDropColumn implements Alterable { public AlterTableDropColumn(String columnName) { _columnName = columnName; } @Override public String getSQL(String tableName) { StringBundler sb = new StringBundler(4); sb.append("alter table "); sb.append(tableName); sb.append(" drop column "); sb.append(_columnName); return sb.toString(); } @Override public boolean shouldAddIndex(Collection<String> columnNames) { return false; } @Override public boolean shouldDropIndex(Collection<String> columnNames) { return Alterable.containsIgnoreCase(columnNames, _columnName); } private final String _columnName; } protected void alter(Class<?> tableClass, Alterable... alterables) throws Exception { try (LoggingTimer loggingTimer = new LoggingTimer()) { String tableName = getTableName(tableClass); DatabaseMetaData databaseMetaData = connection.getMetaData(); DBInspector dbInspector = new DBInspector(connection); try (ResultSet rs1 = databaseMetaData.getPrimaryKeys(dbInspector.getCatalog(), dbInspector.getSchema(), tableName); ResultSet rs2 = databaseMetaData.getIndexInfo(dbInspector.getCatalog(), dbInspector.getSchema(), dbInspector.normalizeName(tableName), false, false)) { Set<String> primaryKeyNames = new HashSet<>(); while (rs1.next()) { String primaryKeyName = StringUtil.toUpperCase(rs1.getString("PK_NAME")); if (primaryKeyName != null) { primaryKeyNames.add(primaryKeyName); } } Map<String, Set<String>> columnNamesMap = new HashMap<>(); while (rs2.next()) { String indexName = StringUtil.toUpperCase(rs2.getString("INDEX_NAME")); if ((indexName == null) || primaryKeyNames.contains(indexName)) { continue; } Set<String> columnNames = columnNamesMap.get(indexName); if (columnNames == null) { columnNames = new HashSet<>(); columnNamesMap.put(indexName, columnNames); } columnNames.add(StringUtil.toUpperCase(rs2.getString("COLUMN_NAME"))); } for (Alterable alterable : alterables) { for (Map.Entry<String, Set<String>> entry : columnNamesMap.entrySet()) { if (alterable.shouldDropIndex(entry.getValue())) { runSQL(StringBundler.concat("drop index ", entry.getKey(), " on ", tableName)); } } runSQL(alterable.getSQL(tableName)); List<ObjectValuePair<String, IndexMetadata>> objectValuePairs = getIndexesSQL( tableClass.getClassLoader(), tableName); if (objectValuePairs == null) { continue; } for (ObjectValuePair<String, IndexMetadata> objectValuePair : objectValuePairs) { IndexMetadata indexMetadata = objectValuePair.getValue(); if (alterable.shouldAddIndex(Arrays.asList(indexMetadata.getColumnNames()))) { runSQLTemplateString(objectValuePair.getKey(), false, true); } } } } catch (SQLException sqle) { if (_log.isWarnEnabled()) { _log.warn(StringBundler.concat("Attempting to upgrade table ", tableName, " by recreating the table due to: ", sqle.getMessage())); } Field tableColumnsField = tableClass.getField("TABLE_COLUMNS"); Field tableSQLCreateField = tableClass.getField("TABLE_SQL_CREATE"); Field tableSQLAddIndexesField = tableClass.getField("TABLE_SQL_ADD_INDEXES"); upgradeTable(tableName, (Object[][]) tableColumnsField.get(null), (String) tableSQLCreateField.get(null), (String[]) tableSQLAddIndexesField.get(null)); if (_log.isWarnEnabled()) { _log.warn("Successfully recreated and upgraded table " + tableName); } } } } protected abstract void doUpgrade() throws Exception; protected List<ObjectValuePair<String, IndexMetadata>> getIndexesSQL(ClassLoader classLoader, String tableName) throws IOException { if (!PortalClassLoaderUtil.isPortalClassLoader(classLoader)) { try (InputStream is = classLoader.getResourceAsStream("META-INF/sql/indexes.sql")) { if (is == null) { return null; } List<ObjectValuePair<String, IndexMetadata>> objectValuePairs = new ArrayList<>(); try (Reader reader = new InputStreamReader(is); UnsyncBufferedReader unsyncBufferedReader = new UnsyncBufferedReader(reader)) { String line = null; while ((line = unsyncBufferedReader.readLine()) != null) { line = line.trim(); if (line.isEmpty()) { continue; } IndexMetadata indexMetadata = IndexMetadataFactoryUtil.createIndexMetadata(line); if (tableName.equals(indexMetadata.getTableName())) { objectValuePairs.add(new ObjectValuePair<>(line, indexMetadata)); } } } return objectValuePairs; } } if (!_portalIndexesSQL.isEmpty()) { return _portalIndexesSQL.get(tableName); } try (InputStream is = classLoader .getResourceAsStream("com/liferay/portal/tools/sql/dependencies/indexes.sql"); Reader reader = new InputStreamReader(is); UnsyncBufferedReader unsyncBufferedReader = new UnsyncBufferedReader(reader)) { String line = null; while ((line = unsyncBufferedReader.readLine()) != null) { line = line.trim(); if (line.isEmpty()) { continue; } IndexMetadata indexMetadata = IndexMetadataFactoryUtil.createIndexMetadata(line); List<ObjectValuePair<String, IndexMetadata>> objectValuePairs = _portalIndexesSQL .get(indexMetadata.getTableName()); if (objectValuePairs == null) { objectValuePairs = new ArrayList<>(); _portalIndexesSQL.put(indexMetadata.getTableName(), objectValuePairs); } objectValuePairs.add(new ObjectValuePair<>(line, indexMetadata)); } } return _portalIndexesSQL.get(tableName); } protected Map<String, Integer> getTableColumnsMap(Class<?> tableClass) throws Exception { Field tableNameField = tableClass.getField("TABLE_COLUMNS_MAP"); return (Map<String, Integer>) tableNameField.get(null); } protected String getTableName(Class<?> tableClass) throws Exception { Field tableNameField = tableClass.getField("TABLE_NAME"); return (String) tableNameField.get(null); } protected long increment() { DB db = DBManagerUtil.getDB(); return db.increment(); } protected long increment(String name) { DB db = DBManagerUtil.getDB(); return db.increment(name); } protected long increment(String name, int size) { DB db = DBManagerUtil.getDB(); return db.increment(name, size); } protected boolean isPortal62TableName(String tableName) { return _portal62TableNames.contains(StringUtil.toLowerCase(tableName)); } protected boolean isSkipUpgradeProcess() throws Exception { return false; } protected boolean isSupportsAlterColumnName() { DB db = DBManagerUtil.getDB(); return db.isSupportsAlterColumnName(); } protected boolean isSupportsAlterColumnType() { DB db = DBManagerUtil.getDB(); return db.isSupportsAlterColumnType(); } protected boolean isSupportsStringCaseSensitiveQuery() { DB db = DBManagerUtil.getDB(); return db.isSupportsStringCaseSensitiveQuery(); } protected boolean isSupportsUpdateWithInnerJoin() { DB db = DBManagerUtil.getDB(); return db.isSupportsUpdateWithInnerJoin(); } protected void upgradeTable(String tableName, Object[][] tableColumns) throws Exception { UpgradeTable upgradeTable = UpgradeTableFactoryUtil.getUpgradeTable(tableName, tableColumns); upgradeTable.updateTable(); } protected void upgradeTable(String tableName, Object[][] tableColumns, String createSQL, String[] indexesSQL, UpgradeColumn... upgradeColumns) throws Exception { try (LoggingTimer loggingTimer = new LoggingTimer(tableName)) { UpgradeTable upgradeTable = UpgradeTableFactoryUtil.getUpgradeTable(tableName, tableColumns, upgradeColumns); upgradeTable.setCreateSQL(createSQL); upgradeTable.setIndexesSQL(indexesSQL); upgradeTable.updateTable(); } } private static final Log _log = LogFactoryUtil.getLog(UpgradeProcess.class); private static final Set<String> _portal62TableNames = new HashSet<>(Arrays.asList("account_", "address", "announcementsdelivery", "announcementsentry", "announcementsflag", "assetcategory", "assetcategoryproperty", "assetentries_assetcategories", "assetentries_assettags", "assetentry", "assetlink", "assettag", "assettagstats", "assetvocabulary", "backgroundtask", "blogsentry", "blogsstatsuser", "bookmarksentry", "bookmarksfolder", "browsertracker", "calevent", "classname_", "clustergroup", "company", "contact_", "counter", "country", "ddlrecord", "ddlrecordset", "ddlrecordversion", "ddmcontent", "ddmstoragelink", "ddmstructure", "ddmstructurelink", "ddmtemplate", "dlcontent", "dlfileentry", "dlfileentrymetadata", "dlfileentrytype", "dlfileentrytypes_dlfolders", "dlfilerank", "dlfileshortcut", "dlfileversion", "dlfolder", "dlsyncevent", "emailaddress", "expandocolumn", "expandorow", "expandotable", "expandovalue", "exportimportconfiguration", "group_", "groups_orgs", "groups_roles", "groups_usergroups", "image", "journalarticle", "journalarticleimage", "journalarticleresource", "journalcontentsearch", "journalfeed", "journalfolder", "journalstructure", "journaltemplate", "layout", "layoutbranch", "layoutfriendlyurl", "layoutprototype", "layoutrevision", "layoutset", "layoutsetbranch", "layoutsetprototype", "listtype", "lock_", "mbban", "mbcategory", "mbdiscussion", "mbmailinglist", "mbmessage", "mbstatsuser", "mbthread", "mbthreadflag", "mdraction", "mdrrule", "mdrrulegroup", "mdrulegroupinstance", "membershiprequest", "organization_", "orggrouprole", "orglabor", "passwordpolicy", "passwordpolicyrel", "passwordtracker", "phone", "pluginsetting", "pollschoice", "pollsquestion", "pollsvote", "portalpreferences", "portlet", "portletitem", "portletpreferences", "ratingsentry", "ratingsstats", "recentlayoutbranch", "recentlayoutrevision", "recentlayoutsetbranch", "region", "release_", "repository", "repositoryentry", "resourceaction", "resourceblock", "resourceblockpermission", "resourcepermission", "resourcetypepermission", "role_", "servicecomponent", "socialactivity", "socialactivityachievement", "socialactivitycounter", "socialactivitylimit", "socialactivityset", "socialactivitysetting", "socialrelation", "socialrequest", "subscription", "systemevent", "team", "ticket", "trashentry", "trashversion", "usernotificationdelivery", "user_", "usergroup", "usergroupgrouprole", "usergrouprole", "usergroups_teams", "useridmapper", "usernotificationevent", "users_groups", "users_orgs", "users_roles", "users_teams", "users_usergroups", "usertracker", "usertrackerpath", "virtualhost", "webdavprops", "website", "wikinode", "wikipage", "wikipageresource", "workflowdefinitionlink", "workflowinstancelink")); private static final Map<String, List<ObjectValuePair<String, IndexMetadata>>> _portalIndexesSQL = new HashMap<>(); }