/**
* 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.tools;
import com.liferay.portal.dao.orm.common.SQLTransformer;
import com.liferay.portal.events.StartupHelperUtil;
import com.liferay.portal.kernel.cache.CacheRegistryUtil;
import com.liferay.portal.kernel.cache.MultiVMPoolUtil;
import com.liferay.portal.kernel.dao.db.DB;
import com.liferay.portal.kernel.dao.db.DBManagerUtil;
import com.liferay.portal.kernel.dao.jdbc.DataAccess;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.model.Release;
import com.liferay.portal.kernel.model.ReleaseConstants;
import com.liferay.portal.kernel.module.framework.ModuleServiceLifecycle;
import com.liferay.portal.kernel.process.ClassPathUtil;
import com.liferay.portal.kernel.service.ClassNameLocalServiceUtil;
import com.liferay.portal.kernel.service.ReleaseLocalServiceUtil;
import com.liferay.portal.kernel.service.ResourceActionLocalServiceUtil;
import com.liferay.portal.kernel.util.ReleaseInfo;
import com.liferay.portal.kernel.util.StringBundler;
import com.liferay.portal.kernel.util.Time;
import com.liferay.portal.transaction.TransactionsUtil;
import com.liferay.portal.util.InitUtil;
import com.liferay.portal.util.PropsValues;
import com.liferay.registry.Registry;
import com.liferay.registry.RegistryUtil;
import com.liferay.registry.ServiceRegistrar;
import com.liferay.util.dao.orm.CustomSQLUtil;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang.time.StopWatch;
/**
* @author Michael C. Han
* @author Brian Wing Shun Chan
*/
public class DBUpgrader {
public static void checkRequiredBuildNumber(int requiredBuildNumber)
throws PortalException {
int buildNumber = ReleaseLocalServiceUtil.getBuildNumberOrCreate();
if (buildNumber > ReleaseInfo.getParentBuildNumber()) {
StringBundler sb = new StringBundler(6);
sb.append("Attempting to deploy an older Liferay Portal version. ");
sb.append("Current build number is ");
sb.append(buildNumber);
sb.append(" and attempting to deploy number ");
sb.append(ReleaseInfo.getParentBuildNumber());
sb.append(".");
throw new IllegalStateException(sb.toString());
}
else if (buildNumber < requiredBuildNumber) {
String msg =
"You must first upgrade to Liferay Portal " +
requiredBuildNumber;
System.out.println(msg);
throw new RuntimeException(msg);
}
}
public static void main(String[] args) {
try {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ClassPathUtil.initializeClassPaths(null);
InitUtil.initWithSpring(true, false);
upgrade();
verify();
_registerModuleServiceLifecycle("database.initialized");
InitUtil.registerContext();
_registerModuleServiceLifecycle("portal.initialized");
System.out.println(
"\nCompleted Liferay core upgrade and verify processes in " +
(stopWatch.getTime() / Time.SECOND) + " seconds");
System.out.println(
"Running modules upgrades. Connect to Gogo shell to check " +
"the status.");
}
catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
public static void upgrade() throws Exception {
// Disable database caching before upgrade
if (_log.isDebugEnabled()) {
_log.debug("Disable cache registry");
}
CacheRegistryUtil.setActive(false);
// Check required build number
checkRequiredBuildNumber(ReleaseInfo.RELEASE_5_2_3_BUILD_NUMBER);
// Upgrade
int buildNumber = ReleaseLocalServiceUtil.getBuildNumberOrCreate();
if (_log.isDebugEnabled()) {
_log.debug("Update build " + buildNumber);
}
_checkPermissionAlgorithm();
_checkReleaseState(_getReleaseState());
if (PropsValues.UPGRADE_DATABASE_TRANSACTIONS_DISABLED) {
TransactionsUtil.disableTransactions();
}
try {
StartupHelperUtil.upgradeProcess(buildNumber);
}
catch (Exception e) {
_updateReleaseState(ReleaseConstants.STATE_UPGRADE_FAILURE);
throw e;
}
finally {
if (PropsValues.UPGRADE_DATABASE_TRANSACTIONS_DISABLED) {
TransactionsUtil.enableTransactions();
}
}
// Reload SQL
CustomSQLUtil.reloadCustomSQL();
SQLTransformer.reloadSQLTransformer();
// Update company key
if (StartupHelperUtil.isUpgraded()) {
if (_log.isDebugEnabled()) {
_log.debug("Update company key");
}
_updateCompanyKey();
}
// Check class names
if (_log.isDebugEnabled()) {
_log.debug("Check class names");
}
ClassNameLocalServiceUtil.checkClassNames();
// Check resource actions
if (_log.isDebugEnabled()) {
_log.debug("Check resource actions");
}
ResourceActionLocalServiceUtil.checkResourceActions();
// Clear the caches only if the upgrade process was run
if (_log.isDebugEnabled()) {
_log.debug("Clear cache if upgrade process was run");
}
if (StartupHelperUtil.isUpgraded()) {
MultiVMPoolUtil.clear();
}
}
public static void verify() throws Exception {
// Check release
Release release = ReleaseLocalServiceUtil.fetchRelease(
ReleaseConstants.DEFAULT_SERVLET_CONTEXT_NAME);
if (release == null) {
release = ReleaseLocalServiceUtil.addRelease(
ReleaseConstants.DEFAULT_SERVLET_CONTEXT_NAME,
ReleaseInfo.getParentBuildNumber());
}
_checkReleaseState(release.getState());
// Update indexes
if (PropsValues.DATABASE_INDEXES_UPDATE_ON_STARTUP) {
StartupHelperUtil.setDropIndexes(true);
StartupHelperUtil.updateIndexes();
}
else if (StartupHelperUtil.isUpgraded()) {
StartupHelperUtil.updateIndexes();
}
// Verify
if (PropsValues.VERIFY_DATABASE_TRANSACTIONS_DISABLED) {
TransactionsUtil.disableTransactions();
}
boolean newBuildNumber = false;
if (ReleaseInfo.getBuildNumber() > release.getBuildNumber()) {
newBuildNumber = true;
}
try {
StartupHelperUtil.verifyProcess(
newBuildNumber, release.isVerified());
}
catch (Exception e) {
_updateReleaseState(ReleaseConstants.STATE_VERIFY_FAILURE);
_log.error(
"Unable to execute verify process: " + e.getMessage(), e);
throw e;
}
finally {
if (PropsValues.VERIFY_DATABASE_TRANSACTIONS_DISABLED) {
TransactionsUtil.enableTransactions();
}
}
// Update indexes
if (PropsValues.DATABASE_INDEXES_UPDATE_ON_STARTUP ||
StartupHelperUtil.isUpgraded()) {
StartupHelperUtil.updateIndexes(false);
}
// Update release
boolean verified = StartupHelperUtil.isVerified();
if (release.isVerified()) {
verified = true;
}
release = ReleaseLocalServiceUtil.updateRelease(
release.getReleaseId(), ReleaseInfo.getVersion(),
ReleaseInfo.getParentBuildNumber(), ReleaseInfo.getBuildDate(),
verified);
// Enable database caching after verify
CacheRegistryUtil.setActive(true);
// Register release service
Registry registry = RegistryUtil.getRegistry();
ServiceRegistrar<Release> serviceRegistrar =
registry.getServiceRegistrar(Release.class);
Map<String, Object> properties = new HashMap<>();
properties.put("build.date", release.getBuildDate());
properties.put("build.number", release.getBuildNumber());
properties.put("servlet.context.name", release.getServletContextName());
serviceRegistrar.registerService(Release.class, release, properties);
}
private static void _checkPermissionAlgorithm() throws Exception {
long count = _getResourceCodesCount();
if (count == 0) {
return;
}
StringBundler sb = new StringBundler(8);
sb.append("Permission conversion to algorithm 6 has not been ");
sb.append("completed. Please complete the conversion prior to ");
sb.append("starting the portal. The conversion process is available ");
sb.append("in portal versions starting with ");
sb.append(ReleaseInfo.RELEASE_5_2_3_BUILD_NUMBER);
sb.append(" and prior to ");
sb.append(ReleaseInfo.RELEASE_6_2_0_BUILD_NUMBER);
sb.append(".");
throw new IllegalStateException(sb.toString());
}
private static void _checkReleaseState(int state) throws Exception {
if (state == ReleaseConstants.STATE_GOOD) {
return;
}
StringBundler sb = new StringBundler(6);
sb.append("The database contains changes from a previous upgrade ");
sb.append("attempt that failed. Please restore the old database and ");
sb.append("file system and retry the upgrade. A patch may be ");
sb.append("required if the upgrade failed due to a bug or an ");
sb.append("unforeseen data permutation that resulted from a corrupt ");
sb.append("database.");
throw new IllegalStateException(sb.toString());
}
private static int _getReleaseState() throws Exception {
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
con = DataAccess.getConnection();
ps = con.prepareStatement(
"select state_ from Release_ where releaseId = ?");
ps.setLong(1, ReleaseConstants.DEFAULT_ID);
rs = ps.executeQuery();
if (rs.next()) {
return rs.getInt("state_");
}
throw new IllegalArgumentException(
"No Release exists with the primary key " +
ReleaseConstants.DEFAULT_ID);
}
finally {
DataAccess.cleanUp(con, ps, rs);
}
}
private static long _getResourceCodesCount() throws Exception {
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
con = DataAccess.getConnection();
ps = con.prepareStatement("select count(*) from ResourceCode");
rs = ps.executeQuery();
if (rs.next()) {
int count = rs.getInt(1);
return count;
}
return 0;
}
catch (Exception e) {
return 0;
}
finally {
DataAccess.cleanUp(con, ps, rs);
}
}
private static void _registerModuleServiceLifecycle(
String moduleServiceLifecycle) {
Registry registry = RegistryUtil.getRegistry();
Map<String, Object> properties = new HashMap<>();
properties.put("module.service.lifecycle", moduleServiceLifecycle);
properties.put("service.vendor", ReleaseInfo.getVendor());
properties.put("service.version", ReleaseInfo.getVersion());
registry.registerService(
ModuleServiceLifecycle.class, new ModuleServiceLifecycle() {},
properties);
}
private static void _updateCompanyKey() throws Exception {
DB db = DBManagerUtil.getDB();
db.runSQL("update Company set key_ = null");
}
private static void _updateReleaseState(int state) throws Exception {
Connection con = null;
PreparedStatement ps = null;
try {
con = DataAccess.getConnection();
ps = con.prepareStatement(
"update Release_ set modifiedDate = ?, state_ = ? where " +
"releaseId = ?");
ps.setDate(1, new Date(System.currentTimeMillis()));
ps.setInt(2, state);
ps.setLong(3, ReleaseConstants.DEFAULT_ID);
ps.executeUpdate();
}
finally {
DataAccess.cleanUp(con, ps);
}
}
private static final Log _log = LogFactoryUtil.getLog(DBUpgrader.class);
}