org.efaps.update.Install Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of efaps-kernel Show documentation
Show all versions of efaps-kernel Show documentation
eFaps is a framework used to map objects with or without attached files to
a relational database and optional file systems (only for attaches files). Configurable access control can be provided down to object and attribute level depending on implementation and use case. Depending on requirements, events (like triggers) allow to implement business logic and to separate business logic from user interface.
The framework includes integrations (e.g. webdav, full text search) and a web application as 'simple' configurable user interface. Some best practises, example web application modules (e.g. team work module) support administrators and implementers using this framework.
/*
* Copyright 2003 - 2011 The eFaps Team
*
* 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.
*
* Revision: $Rev: 6210 $
* Last Changed: $Date: 2011-02-18 16:02:56 -0500 (Fri, 18 Feb 2011) $
* Last Changed By: $Author: [email protected] $
*/
package org.efaps.update;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.jexl.JexlContext;
import org.apache.commons.jexl.JexlHelper;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.efaps.ci.CIAdminCommon;
import org.efaps.db.Context;
import org.efaps.db.MultiPrintQuery;
import org.efaps.db.QueryBuilder;
import org.efaps.update.schema.datamodel.SQLTableUpdate;
import org.efaps.update.util.InstallationException;
import org.efaps.util.EFapsException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;
/**
* TODO description.
*
* @author The eFaps Team
* @version $Id: Install.java 6210 2011-02-18 21:02:56Z [email protected] $
*/
public class Install
{
/**
* Logging instance used to give logging information of this class.
*/
private static final Logger LOG = LoggerFactory.getLogger(SQLTableUpdate.class);
/**
* All defined file urls which are updated.
*
* @see #addFile(URL, String)
*/
private final List files = new ArrayList();
/**
* Flag to store that the cache is initialised.
*
* @see #initialise
* @see #addURL
*/
private boolean initialised = false;
/**
* Cache with all update instances (loaded from the list of {@link #urls}).
*
* @see #initialise
* @see #install
*/
private final Map, List> cache
= new HashMap, List>();
/**
* Installs the XML update scripts of the schema definitions for this
* version defined in {@link #number}. The install itself is done for given
* version normally in one big transaction. If the database does not support
* to big transactions (method
* {@link org.efaps.db.databases.AbstractDatabase#supportsBigTransactions()},
* each modification of one update is committed within small single
* transactions.
*
* @param _number number to install
* @param _latestNumber latest version number to install (e..g. defined
* in the version.xml file)
* @param _ignoredSteps set of ignored life cycle steps which are not
* executed
* @throws InstallationException on error
* @see org.efaps.db.databases.AbstractDatabase#supportsBigTransactions()
*/
@SuppressWarnings("unchecked")
public void install(final Long _number,
final Long _latestNumber,
final Set _ignoredSteps)
throws InstallationException
{
final boolean bigTrans = Context.getDbType().supportsBigTransactions();
final String user;
try {
user = (Context.getThreadContext().getPerson() != null)
? Context.getThreadContext().getPerson().getName()
: null;
} catch (final EFapsException e) {
throw new InstallationException("No context in this thread defined!", e);
}
// initialize cache
initialise();
// initialize JexlContext (used to evaluate version)
final JexlContext jexlContext = JexlHelper.createContext();
if (_number != null) {
jexlContext.getVars().put("version", _number);
}
if (_latestNumber != null) {
jexlContext.getVars().put("latest", _latestNumber);
}
// loop through all life cycle steps
for (final UpdateLifecycle step : getUpdateLifecycles()) {
if (!_ignoredSteps.contains(step)) {
if (Install.LOG.isInfoEnabled()) {
Install.LOG.info("..Running Lifecycle step " + step);
}
for (final Map.Entry, List> entry : this.cache.entrySet()) {
for (final IUpdate update : entry.getValue()) {
update.updateInDB(jexlContext, step);
if (!bigTrans) {
try {
Context.commit();
} catch (final EFapsException e) {
throw new InstallationException("Transaction commit failed", e);
}
try {
Context.begin(user);
} catch (final EFapsException e) {
throw new InstallationException("Transaction start failed", e);
}
}
}
}
} else if (Install.LOG.isInfoEnabled()) {
Install.LOG.info("..Skipped Lifecycle step " + step);
}
}
}
/**
* Method to get all UpdateLifecycle in an ordered List.
* @return ordered List of all UpdateLifecycle
*/
private List getUpdateLifecycles()
{
final List ret = new ArrayList();
for (final UpdateLifecycle cycle : UpdateLifecycle.values()) {
ret.add(cycle);
}
Collections.sort(ret, new Comparator() {
public int compare(final UpdateLifecycle _cycle1,
final UpdateLifecycle _cycle2)
{
return _cycle1.getOrder().compareTo(_cycle2.getOrder());
}
});
return ret;
}
/**
* All installation files are updated. For each file, the installation and
* latest version is evaluated depending from all installed version and the
* defined application in the XML update file. The installation version is
* the same as the latest version of the application.
*
* @throws InstallationException if update failed
*/
@SuppressWarnings("unchecked")
public void updateLatest()
throws InstallationException
{
final boolean bigTrans = Context.getDbType().supportsBigTransactions();
final String user;
try {
user = (Context.getThreadContext().getPerson() != null)
? Context.getThreadContext().getPerson().getName()
: null;
} catch (final EFapsException e) {
throw new InstallationException("No context in this thread defined!", e);
}
// initialize cache
initialise();
// get for all applications the latest version
final Map versions = getLatestVersions();
// loop through all life cycle steps
for (final UpdateLifecycle step : getUpdateLifecycles()) {
if (Install.LOG.isInfoEnabled()) {
Install.LOG.info("..Running Lifecycle step " + step);
}
for (final Map.Entry, List> entry
: this.cache.entrySet()) {
for (final IUpdate update : entry.getValue()) {
final Integer latestVersion = versions.get(update.getFileApplication());
// initialize JexlContext (used to evaluate version)
final JexlContext jexlContext = JexlHelper.createContext();
if (latestVersion != null) {
jexlContext.getVars().put("version", latestVersion);
jexlContext.getVars().put("latest", latestVersion);
}
// and create
update.updateInDB(jexlContext, step);
if (!bigTrans) {
if (!bigTrans) {
try {
Context.commit();
} catch (final EFapsException e) {
throw new InstallationException("Transaction commit failed", e);
}
try {
Context.begin(user);
} catch (final EFapsException e) {
throw new InstallationException("Transaction start failed", e);
}
}
}
}
}
}
}
/**
* Load the already installed versions for this application from eFaps. The
* method must be called within a Context begin and commit (it is not done
* itself in this method!
* @return Map containing the versions
* @throws InstallationException on error
*/
public Map getLatestVersions()
throws InstallationException
{
final Map versions = new HashMap();
if (CIAdminCommon.Version.getType() != null) {
try {
final QueryBuilder queryBldr = new QueryBuilder(CIAdminCommon.Version);
final MultiPrintQuery multi = queryBldr.getPrint();
multi.addAttribute(CIAdminCommon.Version.Name, CIAdminCommon.Version.Revision);
multi.executeWithoutAccessCheck();
while (multi.next()) {
final String name = multi.getAttribute(CIAdminCommon.Version.Name);
final Integer revision = multi.getAttribute(CIAdminCommon.Version.Revision);
if (!versions.containsKey(name) || (versions.get(name) < revision)) {
versions.put(name, revision);
}
}
} catch (final EFapsException e) {
throw new InstallationException("Latest version could not be found", e);
}
}
return versions;
}
/**
* Reads all XML update files and parses them.
*
* @see #initialised
* @throws InstallationException on error
*/
protected void initialise()
throws InstallationException
{
if (!this.initialised) {
this.initialised = true;
this.cache.clear();
for (final FileType fileType : FileType.values()) {
if (fileType == FileType.XML) {
for (final InstallFile file : this.files) {
if (file.getType() == fileType) {
final SaxHandler handler = new SaxHandler();
try {
final IUpdate elem = handler.parse(file.getUrl());
List list = this.cache.get(elem.getClass());
if (list == null) {
list = new ArrayList();
this.cache.put(elem.getClass(), list);
}
list.add(handler.getUpdate());
} catch (final SAXException e) {
throw new InstallationException("initialise()", e);
} catch (final IOException e) {
throw new InstallationException("initialise()", e);
}
}
}
} else {
for (final Class extends AbstractUpdate> updateClass : fileType.getClazzes()) {
final List list = new ArrayList();
this.cache.put(updateClass, list);
Method method = null;
try {
method = updateClass.getMethod("readFile", URL.class);
} catch (final SecurityException e) {
throw new InstallationException("initialise()", e);
} catch (final NoSuchMethodException e) {
throw new InstallationException("initialise()", e);
}
for (final InstallFile file : this.files) {
if (file.getType() == fileType) {
Object obj = null;
try {
obj = method.invoke(null, file.getUrl());
} catch (final IllegalArgumentException e) {
throw new InstallationException("initialise()", e);
} catch (final IllegalAccessException e) {
throw new InstallationException("initialise()", e);
} catch (final InvocationTargetException e) {
throw new InstallationException("initialise()", e);
}
if (obj != null) {
list.add((AbstractUpdate) obj);
}
}
}
}
}
}
}
}
/**
* Appends a new file defined through an URL and the string representation
* of the file type.
*
* @param _url URL of the file to append
* @param _type type of the file
* @see #files
* @see #initialised
* @see #addFile(URL, FileType) method called to add the URL after convert
* the string representation of the type to a file type instance
*/
public void addFile(final URL _url,
final String _type)
{
addFile(_url, FileType.getFileTypeByType(_type));
}
/**
* Appends a new file defined through an URL. The initialized flag
* {@link #initialized} is automatically reseted.
*
* @param _url URL of the file to add
* @param _fileType file type of the file to add
*/
public void addFile(final URL _url,
final FileType _fileType)
{
this.files.add(new InstallFile(_url, _fileType));
this.initialised = false;
}
/**
* This is the getter method for the instance variable {@link #files}.
*
* @return value of instance variable {@link #files}
*/
public List getFiles()
{
return this.files;
}
/**
* Returns a string representation with values of all instance variables.
*
* @return string representation of this Application
*/
@Override
public String toString()
{
return new ToStringBuilder(this)
.append("urls", this.files)
.toString();
}
/**
* Class is used as a container for one file that must be installed.
*/
private class InstallFile
{
/**
* URL to the file.
*/
private final URL url;
/**
* Type of the file.
*/
private final FileType type;
/**
* @param _url Url to the file
* @param _type Type of the file.
*/
public InstallFile(final URL _url,
final FileType _type)
{
this.url = _url;
this.type = _type;
}
/**
* This is the getter method for the instance variable {@link #url}.
*
* @return value of instance variable {@link #url}
*/
public URL getUrl()
{
return this.url;
}
/**
* This is the getter method for the instance variable {@link #type}.
*
* @return value of instance variable {@link #type}
*/
public FileType getType()
{
return this.type;
}
}
}