
org.efaps.update.version.ApplicationVersion Maven / Gradle / Ivy
/*
* Copyright 2003 - 2013 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: 8908 $
* Last Changed: $Date: 2013-02-20 16:18:41 -0500 (Wed, 20 Feb 2013) $
* Last Changed By: $Author: [email protected] $
*/
package org.efaps.update.version;
import groovy.lang.Binding;
import groovy.lang.GroovyClassLoader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.digester3.annotations.rules.CallMethod;
import org.apache.commons.digester3.annotations.rules.CallParam;
import org.apache.commons.digester3.annotations.rules.ObjectCreate;
import org.apache.commons.digester3.annotations.rules.SetProperty;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.efaps.admin.program.esjp.EFapsClassLoader;
import org.efaps.db.Context;
import org.efaps.update.Install;
import org.efaps.update.Profile;
import org.efaps.update.UpdateLifecycle;
import org.efaps.update.util.InstallationException;
import org.efaps.util.EFapsException;
import org.mozilla.javascript.ImporterTopLevel;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Defines one version of the application to install.
*
* @author The eFaps Team
* @version $Id: ApplicationVersion.java 8908 2013-02-20 21:18:41Z [email protected] $
* TODO: in case of a script: it must be possible to deactivate the context
*/
@ObjectCreate(pattern = "install/version")
public class ApplicationVersion
implements Comparable
{
/**
* Logging instance used to give logging information of this class.
*/
private static final Logger LOG = LoggerFactory.getLogger(ApplicationVersion.class);
/**
* The number of the version is stored in this instance variable.
*
* @see #setNumber
* @see #getNumber
*/
@SetProperty(pattern = "install/version", attributeName = "number")
private Long number = Long.valueOf(0);
/**
* Store the information weather a compile must be done after installing
* this version.
*
* @see #setCompile(boolean)
*/
@SetProperty(pattern = "install/version", attributeName = "compile")
private boolean compile = false;
/**
* Is a login for this version needed? This means if a new transaction is
* started, a login with given user is made. The default value is
* true.
*
* @see #setLoginNeeded(boolean)
*/
@SetProperty(pattern = "install/version", attributeName = "login")
private boolean loginNeeded = true;
/**
* Is a reload cache for this version needed? This means before the
* installation of this version starts, a reload cache is done. The default
* value is true.
*
* @see #setReloadCacheNeeded
*/
@SetProperty(pattern = "install/version", attributeName = "reloadCache")
private boolean reloadCacheNeeded = true;
/**
* List of all scripts for this version.
*
* @see #addScript(String, String, String, String)
*/
private final List scripts = new ArrayList();
/**
* Description of this version.
*
* @see #appendDescription(String)
* @see #getDescription()
*/
private final StringBuilder description = new StringBuilder();
/**
* Set of ignored life cycle steps.
*
* @see #addIgnoredStep(String)
*/
private final Set ignoredSteps = new HashSet();
/**
* Application this version belongs to.
*/
private Application application;
/**
* Installs the XML update scripts of the schema definitions for this
* version defined in {@link #number}.
*
* @param _install install instance with all cached XML definitions
* @param _latestVersionNumber latest version number (defined in the
* version.xml file)
* @param _profiles profiles to be applied
* @param _userName name of logged in user
* @param _password password of logged in user
* @throws InstallationException on error
*/
public void install(final Install _install,
final long _latestVersionNumber,
final Set _profiles,
final String _userName,
final String _password)
throws InstallationException
{
// reload cache if needed
if (this.reloadCacheNeeded) {
this.application.reloadCache();
}
try {
// start transaction (with user name if needed)
if (this.loginNeeded) {
Context.begin(_userName);
} else {
Context.begin();
}
_install.install(this.number, _latestVersionNumber, _profiles, this.ignoredSteps);
// commit transaction
Context.commit();
// execute all scripts
for (final AbstractScript script : this.scripts) {
script.execute(_userName, _password);
}
// Compile esjp's in the database (if the compile flag is set).
if (this.compile) {
this.application.compileAll(_userName, true);
}
} catch (final EFapsException e) {
throw new InstallationException("error in Context", e);
}
}
/**
* Adds a new Script to this version.
*
* @param _code code of script to execute
* @param _type type of the code, groovy, rhino
* @param _name file name of the script
* @param _function name of function which is called
*/
@CallMethod(pattern = "install/version/script")
public void addScript(@CallParam(pattern = "install/version/script") final String _code,
@CallParam(pattern = "install/version/script", attributeName = "type") final String _type,
@CallParam(pattern = "install/version/script", attributeName = "name") final String _name,
@CallParam(pattern = "install/version/script", attributeName = "function")
final String _function)
{
AbstractScript script = null;
if ("rhino".equalsIgnoreCase(_type)) {
script = new RhinoScript(_code, _name, _function);
} else if ("groovy".equalsIgnoreCase(_type)) {
script = new GroovyScript(_code, _name, _function);
}
if (script != null) {
this.scripts.add(script);
}
}
/**
* Append a description for this version.
*
* @param _desc text of description to append
* @see #description
*/
@CallMethod(pattern = "install/version/description")
public void appendDescription(@CallParam(pattern = "install/version/description") final String _desc)
{
if (_desc != null) {
this.description.append(_desc.trim()).append("\n");
}
}
/**
* @param _appl Application
*/
public void setApplication(final Application _appl)
{
this.application = _appl;
}
/**
* The description for this version is returned. If no description exists, a
* zero length description is returned.
*
* @return string value of instance variable {@link #description}
* @see #description
*/
public String getDescription()
{
return this.description.toString().trim();
}
/**
* Appends a step which is ignored within the installation of this version.
*
* @param _step ignored step
* @see #ignoredSteps
*/
@CallMethod(pattern = "install/version/lifecyle/ignore")
public void addIgnoredStep(@CallParam(pattern = "install/version/lifecyle/ignore", attributeName = "step")
final String _step)
{
this.ignoredSteps.add(UpdateLifecycle.valueOf(_step.toUpperCase()));
}
/**
* Compares this application version with the specified application version.
* The method compares the version number of the application version. To do
* this, the method {@link java.lang.Long#compareTo} is called.
*
* @param _compareTo application version instance to compare to
* @return a negative integer, zero, or a positive integer as this
* application version is less than, equal to, or greater than the
* specified application version
* @see java.lang.Long#compareTo
* @see java.lang.Comparable#compareTo
*/
public int compareTo(final ApplicationVersion _compareTo)
{
return new Long(this.number).compareTo(_compareTo.number);
}
/**
* This is the setter method for instance variable {@link #number}.
*
* @param _number new value for instance variable {@link #number}
* @see #number
* @see #getNumber
*/
public void setNumber(final Long _number)
{
this.number = _number;
}
/**
* This is the getter method for instance variable {@link #number}.
*
* @return value of instance variable {@link #number}
* @see #number
* @see #setNumber
*/
public Long getNumber()
{
return this.number;
}
/**
* This is the setter method for instance variable {@link #compile}.
*
* @param _compile new value for instance variable {@link #compile}
* @see #compile
*/
public void setCompile(final boolean _compile)
{
this.compile = _compile;
}
/**
* This is the setter method for instance variable {@link #loginNeeded}.
*
* @param _loginNeeded true means that a login is needed for
* this version
* @see #loginNeeded
*/
public void setLoginNeeded(final boolean _loginNeeded)
{
this.loginNeeded = _loginNeeded;
}
/**
* This is the setter method for instance variable
* {@link #reloadCacheNeeded}.
*
* @param _reloadCacheNeeded true means that the cache must be
* reloaded
* @see #reloadCacheNeeded
*/
public void setReloadCacheNeeded(final boolean _reloadCacheNeeded)
{
this.reloadCacheNeeded = _reloadCacheNeeded;
}
/**
* Returns the complete root URL so that resources in the installation
* package could be fetched. If an installation from the source directory
* is done, the {@link Application#getRootUrl() root URL} is directly
* returned, in the case that an installation is done from a JAR container
* the {@link Application#getRootUrl() root URL} is appended with the name
* of the {@link Application#getRootPackageName() root package name}.
*
* @return complete root URL
* @throws InstallationException if complete root URL could not be prepared
*/
protected URL getCompleteRootUrl()
throws InstallationException
{
try {
final URL url;
if (this.application.getRootPackageName() == null) {
url = this.application.getRootUrl();
} else {
url = new URL(this.application.getRootUrl(), this.application.getRootPackageName());
}
return url;
} catch (final MalformedURLException e) {
throw new InstallationException("Root url could not be prepared", e);
}
}
/**
* 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("number", this.number)
.toString();
}
/**
* Class used to store information of needed called scripts within an
* application version.
*/
private abstract class AbstractScript
{
/**
* Script code to execute.
*/
private final String code;
/**
* File name of the script (within the class path).
*/
private final String fileName;
/**
* Name of called function.
*/
private final String function;
/**
* Constructor to initialize a script.
*
* @param _code script code
* @param _fileName script file name
* @param _function called function name
*/
private AbstractScript(final String _code,
final String _fileName,
final String _function)
{
this.code = (_code == null) || ("".equals(_code.trim())) ? null : _code.trim();
this.fileName = _fileName;
this.function = _function;
}
/**
* Executes this script.
*
* @param _userName name of logged in user
* @param _password password of logged in user
* @throws InstallationException on error
*/
public abstract void execute(final String _userName,
final String _password)
throws InstallationException;
/**
* Getter method for instance variable {@link #code}.
*
* @return value of instance variable {@link #code}
*/
public String getCode()
{
return this.code;
}
/**
* Getter method for instance variable {@link #fileName}.
*
* @return value of instance variable {@link #fileName}
*/
public String getFileName()
{
return this.fileName;
}
/**
* Getter method for instance variable {@link #function}.
*
* @return value of instance variable {@link #function}
*/
public String getFunction()
{
return this.function;
}
}
/**
*Script for groovy.
*/
private final class GroovyScript
extends ApplicationVersion.AbstractScript
{
/**
* Constructor.
* @param _code code
* @param _fileName filename
* @param _function function
*/
private GroovyScript(final String _code,
final String _fileName,
final String _function)
{
super(_code, _fileName, _function);
}
/**
* {@inheritDoc}
* @throws InstallationException if installation failed
* TODO: it must be able to deactivate the CONTEXT
*/
@Override
public void execute(final String _userName,
final String _password)
throws InstallationException
{
boolean commit = false;
try {
try {
Context.begin(_userName);
} catch (final EFapsException e) {
throw new InstallationException("Context could not be started", e);
}
final ClassLoader parent = getClass().getClassLoader();
final EFapsClassLoader efapsClassLoader = new EFapsClassLoader(parent);
final CompilerConfiguration config = new CompilerConfiguration();
config.setClasspathList(ApplicationVersion.this.application.getClassPathElements());
final GroovyClassLoader loader = new GroovyClassLoader(efapsClassLoader, config);
if (getCode() != null) {
final Class> clazz = loader.parseClass(getCode());
groovy.lang.Script go;
try {
go = (groovy.lang.Script) clazz.newInstance();
final Binding binding = new Binding();
binding.setVariable("EFAPS_LOGGER", ApplicationVersion.LOG);
binding.setVariable("EFAPS_USERNAME", _userName);
binding.setVariable("EFAPS_PASSWORD", _userName);
binding.setVariable("EFAPS_ROOTURL", getCompleteRootUrl());
go.setBinding(binding);
final Object[] args = {};
go.invokeMethod("run", args);
} catch (final InstantiationException e) {
throw new InstallationException("InstantiationException in Groovy", e);
} catch (final IllegalAccessException e) {
throw new InstallationException("IllegalAccessException in Groovy", e);
}
}
try {
Context.commit();
} catch (final EFapsException e) {
throw new InstallationException("Tranaction could not be commited", e);
}
commit = true;
} finally {
if (!commit) {
try {
Context.rollback();
} catch (final EFapsException e) {
throw new InstallationException("Tranaction could not be aborted", e);
}
}
}
}
}
/**
* Script for mozilla rhino (Javascript).
*/
private final class RhinoScript
extends ApplicationVersion.AbstractScript
{
/**
* Constructor.
*
* @param _code code
* @param _fileName filename
* @param _function function
*/
private RhinoScript(final String _code,
final String _fileName,
final String _function)
{
super(_code, _fileName, _function);
}
/**
* {@inheritDoc}
*/
@Override
public void execute(final String _userName,
final String _password)
throws InstallationException
{
try {
// create new javascript context
final org.mozilla.javascript.Context javaScriptContext = org.mozilla.javascript.Context.enter();
final Scriptable scope = new ImporterTopLevel(javaScriptContext);
// define the context javascript property
ScriptableObject.putProperty(scope, "javaScriptContext", javaScriptContext);
// define the scope javascript property
ScriptableObject.putProperty(scope, "javaScriptScope", scope);
ScriptableObject.putProperty(scope, "EFAPS_LOGGER",
org.mozilla.javascript.Context.javaToJS(ApplicationVersion.LOG, scope));
ScriptableObject.putProperty(scope, "EFAPS_USERNAME",
org.mozilla.javascript.Context.javaToJS(_userName, scope));
ScriptableObject.putProperty(scope, "EFAPS_PASSWORD",
org.mozilla.javascript.Context.javaToJS(_userName, scope));
ScriptableObject.putProperty(scope,
"EFAPS_ROOTURL",
org.mozilla.javascript.Context.javaToJS(getCompleteRootUrl(), scope));
// evaluate java script file (if defined)
if (getFileName() != null) {
if (ApplicationVersion.LOG.isInfoEnabled()) {
ApplicationVersion.LOG.info("Execute script file '" + getFileName() + "'");
}
final Reader in = new InputStreamReader(
new URL(getCompleteRootUrl(), getFileName()).openStream());
javaScriptContext.evaluateReader(scope, in, getFileName(), 1, null);
in.close();
}
// evaluate script code (if defined)
if (getCode() != null) {
javaScriptContext.evaluateReader(scope, new StringReader(getCode()),
"Executing script code of version " + ApplicationVersion.this.number, 1, null);
}
// evaluate script defined through the reader
if (getFunction() != null) {
if (ApplicationVersion.LOG.isInfoEnabled()) {
ApplicationVersion.LOG.info("Execute script function '" + getFunction() + "'");
}
javaScriptContext.evaluateReader(scope, new StringReader(getFunction()), getFunction(), 1, null);
}
} catch (final IOException e) {
throw new InstallationException("IOException in RhinoScript", e);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy