/**
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you 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 org.jasig.portal.version;
import java.util.Map;
import javax.annotation.Resource;
import org.jasig.portal.shell.PortalShellBuildHelper;
import org.jasig.portal.version.dao.VersionDao;
import org.jasig.portal.version.om.Version;
import org.jasig.portal.version.om.Version.Field;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContextException;
import org.springframework.stereotype.Component;
import com.google.common.collect.ImmutableMap;
/**
* Bean that verifies the product version numbers in the configuration versus the database on startup
*
* @author Eric Dalquist
*/
@Component
public class VersionVerifier implements InitializingBean {
protected final Logger logger = LoggerFactory.getLogger(getClass());
private Map<String, Version> requiredProductVersions;
private VersionDao versionDao;
private Version.Field updatePolicy = Version.Field.LOCAL;
private PortalShellBuildHelper portalShellBuildHelper;
@Resource(name = "productVersions")
public void setRequiredProductVersions(Map<String, Version> requiredProductVersions) {
this.requiredProductVersions = ImmutableMap.copyOf(requiredProductVersions);
}
@Autowired
public void setVersionDao(VersionDao versionDao) {
this.versionDao = versionDao;
}
@Autowired
public void setPortalShellBuildHelper(PortalShellBuildHelper portalShellBuildHelper) {
this.portalShellBuildHelper = portalShellBuildHelper;
}
@Value("${org.jasig.portal.version.autoUpdatePolicy:LOCAL}")
public void setUpdatePolicy(Version.Field updatePolicy) {
if (updatePolicy != null && updatePolicy != Version.Field.LOCAL && updatePolicy != Version.Field.PATCH) {
throw new IllegalArgumentException("Only null, LOCAL, and PATCH updates are allowed");
}
this.updatePolicy = updatePolicy;
}
@Override
public void afterPropertiesSet() throws Exception {
for (final Map.Entry<String, Version> productVersionEntry : this.requiredProductVersions.entrySet()) {
final String product = productVersionEntry.getKey();
final Version dbVersion = this.versionDao.getVersion(product);
if (dbVersion == null) {
throw new ApplicationContextException("No Version exists for " + product + " in the database. Please check the upgrade instructions for this release.");
}
final Version codeVersion = productVersionEntry.getValue();
final Field mostSpecificMatchingField = VersionUtils.getMostSpecificMatchingField(dbVersion, codeVersion);
switch (mostSpecificMatchingField) {
//Versions completely match
case LOCAL: {
logger.info("Software and Database versions are both {} for {}", dbVersion, product);
continue;
}
//Versions match except for local part
case PATCH:
//Versions match except for patch.local part
case MINOR: {
//If db is before code and auto-update is enabled run hibernate-update
final Field upgradeField = mostSpecificMatchingField.getLessImportant();
if (dbVersion.isBefore(codeVersion) &&
this.updatePolicy != null &&
(upgradeField.equals(this.updatePolicy) || upgradeField.isLessImportantThan(this.updatePolicy))) {
logger.info("Automatically updating database from {} to {} for {}", dbVersion, codeVersion, product);
this.portalShellBuildHelper.hibernateUpdate("automated-hibernate-update", product, true, null);
continue;
}
else if (codeVersion.isBefore(dbVersion)) {
//It is ok to run older code on a newer DB within the local/patch range
continue;
}
}
//Versions match except for minor.patch.local part
case MAJOR:
//Versions do not match at all
default: {
if (dbVersion.isBefore(codeVersion)) {
throw new ApplicationContextException("Database Version for " + product + " is " + dbVersion + " but the code version is " + codeVersion + ". Please check the upgrade instructions for this release");
}
else {
throw new ApplicationContextException("Database Version for " + product + " is " + dbVersion + " but the code version is " + codeVersion + ". It is not possible to run ");
}
}
}
}
}
}