/*******************************************************************************
* Copyright 2009, 2010 Innovation Gate GmbH. All Rights Reserved.
*
* This file is part of the OpenWGA server platform.
*
* OpenWGA is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, a special exception is granted by the copyright holders
* of OpenWGA called "OpenWGA plugin exception". You should have received
* a copy of this exception along with OpenWGA in file COPYING.
* If not, see <http://www.openwga.com/gpl-plugin-exception>.
*
* OpenWGA 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenWGA in file COPYING.
* If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
/**
* Represents a plugin file and offers operations to deal with it
*/
package de.innovationgate.wgpublisher.plugins;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.vfs.FileSystemException;
import de.innovationgate.utils.TemporaryFile;
import de.innovationgate.utils.WGUtils;
import de.innovationgate.webgate.api.WGIllegalArgumentException;
import de.innovationgate.wga.common.DesignDirectory;
import de.innovationgate.wga.common.beans.DesignDefinition;
import de.innovationgate.wga.common.beans.csconfig.v1.CSConfig;
import de.innovationgate.wga.common.beans.csconfig.v1.InvalidCSConfigVersionException;
import de.innovationgate.wga.common.beans.csconfig.v1.PluginConfig;
import de.innovationgate.wga.common.beans.csconfig.v1.PluginID;
import de.innovationgate.wga.common.beans.csconfig.v1.PublisherOption;
import de.innovationgate.wgpublisher.SystemContainerManager;
import de.innovationgate.wgpublisher.WGACore;
import de.innovationgate.wgpublisher.plugins.WGAPluginSet.RuntimeContext;
public class WGAPlugin {
public static final int UPDATESTATUS_NEW = 0;
public static final int UPDATESTATUS_UPDATE = 1;
public static final int UPDATESTATUS_INSTALL_PARALLEL = 2;
public static final int UPDATESTATUS_INSTALL_DEACTIVATED = 1;
public static final int UPDATESTATUS_UPDATE_IDENTICAL = 3;
public static Set<String> PLATFORM_PLUGINS = new HashSet<String>();
static {
PLATFORM_PLUGINS.add("de.innovationgate.admin");
PLATFORM_PLUGINS.add("de.innovationgate.app-ui-3-1");
PLATFORM_PLUGINS.add("de.innovationgate.contentmanager");
PLATFORM_PLUGINS.add("de.innovationgate.csmaintenance");
PLATFORM_PLUGINS.add("de.innovationgate.Management");
PLATFORM_PLUGINS.add("de.innovationgate.wga-app-framework");
PLATFORM_PLUGINS.add("de.innovationgate.wgaservices-xfire");
}
public static class Configuration {
private DesignDefinition _syncInfo;
private CSConfig _csConfig;
private String _licenseText;
// Default constructor for serialisation
private Configuration() {
}
public Configuration(DesignDefinition syncInfo, CSConfig csConfig, String licenseText) {
super();
_syncInfo = syncInfo;
_csConfig = csConfig;
_licenseText = licenseText;
}
public CSConfig getCsConfig() {
return _csConfig;
}
public DesignDefinition getSyncInfo() {
return _syncInfo;
}
public String getLicenseText() {
return _licenseText;
}
}
public class InstallationFault {
public static final int ERROR_WRONG_WGA_VERSION = 1;
public static final int ERROR_WRONG_JAVA_VERSION = 2;
public static final int ERROR_DEPENDENCY_NOT_AVAILABLE = 3;
public static final int ERROR_DEPENDENCY_WRONG_VERSION = 4;
public static final int ERROR_DEPENDENCY_INACTIVE = 5;
public static final int ERROR_CIRCULAR_REFERENCE = 6;
public static final int ERROR_INITIALISATION = 7;
private int _error;
// Default constructor for serialisation
private InstallationFault() {
}
public InstallationFault(int error) {
_error = error;
}
public String toErrorMessage() {
if (_error == ERROR_WRONG_JAVA_VERSION) {
return "This plugin needs at least Java Version " + getCsConfig().getPluginConfig().getMinimumJavaVersion().toString();
}
else if (_error == ERROR_WRONG_WGA_VERSION) {
return "This plugin needs at least WGA Version " + getCsConfig().getPluginConfig().getMinimumWGAVersion().toString();
}
else {
return "Unknown error code " + _error;
}
}
public int getError() {
return _error;
}
}
public class DependencyFault extends InstallationFault {
public String toErrorMessage() {
if (getError() == ERROR_DEPENDENCY_NOT_AVAILABLE) {
return "This plugin depends on another WGA Plugin " + _depPluginID.getUniqueName() + " " + _depPluginID.getVersion().toString() + " which is either not installed or not active.";
}
else if (getError() == ERROR_DEPENDENCY_WRONG_VERSION) {
return "This plugin depends on another WGA Plugin " + _depPluginID.getUniqueName() + " " + _depPluginID.getVersion().toString() + " which has a too low version.";
}
else if (getError() == ERROR_DEPENDENCY_INACTIVE) {
return "This plugin depends on another WGA Plugin " + _depPluginID.getUniqueName() + " " + _depPluginID.getVersion().toString() + " which is inactive.";
}
else if (getError() == ERROR_CIRCULAR_REFERENCE) {
return "This plugin causes a circular reference bc. it's dependency tree contains itself. WGA therefor cannot determine a correct order to connect plugins.";
}
else {
return super.toErrorMessage();
}
}
private PluginID _depPluginID;
// Default constructor for serialisation
private DependencyFault() {
}
public DependencyFault(PluginID pluginID, int error) {
super(error);
_depPluginID = pluginID;
}
}
public class InitialisationFault extends InstallationFault {
private String _msg;
public InitialisationFault(String msg) {
super(InstallationFault.ERROR_INITIALISATION);
_msg = msg;
}
// Default constructor for serialisation
private InitialisationFault() {
}
public String toErrorMessage() {
return "Unable to initialize plugin: " + _msg;
}
}
// Persistent fields
private WGAPluginSet _parent = null;
private boolean _active = true;
private boolean _defaultPlugin = false;
private boolean _reconnectDatabase = false;
private String _filePath;
private String _installationKey;
// May not be transient bc. it distinguishes the plugin in a HashSet
private PluginID _pluginID;
private transient Map _mandatoryPlugins;
private transient Map _dependentPlugins;
private transient boolean _valid = true;
private transient WorkspaceOperation _operation;
private transient int _updateStatus = UPDATESTATUS_NEW;
private transient long _fileLastModified;
private transient List _installationFaults;
private transient Configuration _config;
private WGAPlugin() {
}
protected WGAPlugin(WGAPluginSet parent, String path) throws WGIllegalArgumentException, IOException, InvalidPluginException {
_parent = parent;
_filePath = path;
init();
}
public File getPluginFile() {
File file = getParent().getCore().getWGAFile(_filePath);
// Backward compatibility to WGA4: find relative to plugins folder
if (file == null || !file.exists()) {
file = new File(getParent().getPluginsDir(), _filePath);
}
return file;
}
public void init() throws FileSystemException, IOException, InvalidPluginException {
_valid = true;
_mandatoryPlugins = new HashMap();
_dependentPlugins = new HashMap();
_installationFaults = new ArrayList();
try {
validate();
}
catch (InvalidPluginException e) {
_installationFaults.add(new InitialisationFault(e.getMessage()));
throw e;
}
}
public void validate() throws InvalidPluginException {
try {
File file = getPluginFile();
if (file == null || !file.exists()) {
if (isDefaultPlugin()) {
throw new MissingDefaultPluginException(this);
}
else {
throw new InvalidPluginException(this, "The plugin file/directory does not exist (any more): " + _filePath);
}
}
loadMetadata(file);
}
catch (InvalidPluginException e) {
_valid = false;
throw e;
}
}
/**
* Load all metadata about the plugin
* - syncinfo.xml
* - csconfig.xml
* - File last modified time
* @throws InvalidPluginException
*/
private synchronized void loadMetadata(File file) throws InvalidPluginException {
try {
_fileLastModified = file.lastModified();
_config = loadConfiguration(file);
if (_config == null) {
throw new InvalidPluginException(this, "This plugin does not contain a mandatory file: syncinfo.xml/design.xml or csconfig.xml");
}
if (_config.getCsConfig().getPluginConfig() == null) {
throw new InvalidPluginException(this, "The plugin is invalid because its csconfig.xml does not contain a plugin configuration");
}
_pluginID = _config.getCsConfig().getPluginConfig().getId();
}
catch (InvalidCSConfigVersionException e) {
throw new InvalidPluginException(this, "This plugin was developed for a higher WGA version: " + e.getTargetVersion());
}
catch (Exception e) {
throw new InvalidPluginException(this, "Cannot read plugin metadata bc. of exception ",e);
}
}
public static Configuration loadConfiguration(File file) throws FileNotFoundException, IOException, InvalidCSConfigVersionException {
DesignDefinition syncInfo = null;
CSConfig csConfig = null;
String licenseText = null;
// Normal plugin file
if (file.isFile()) {
ZipInputStream zipIn = new ZipInputStream(new FileInputStream(file));
try {
ZipEntry entry;
while ((entry = zipIn.getNextEntry()) != null) {
String entryName = entry.getName();
if (entryName.equals(DesignDirectory.DESIGN_DEFINITION_FILE) || entryName.equals(DesignDirectory.SYNCINFO_FILE)) {
TemporaryFile tempFile = new TemporaryFile("design", zipIn, null);
syncInfo = DesignDefinition.load(tempFile.getFile());
tempFile.delete();
}
else if (entryName.equals(SystemContainerManager.CSCONFIG_PATH)) {
TemporaryFile tempFile = new TemporaryFile("csconfig", zipIn, null);
csConfig = CSConfig.load(tempFile.getFile());
tempFile.delete();
}
else if (entryName.equals(SystemContainerManager.LICENSE_PATH)) {
licenseText = WGUtils.readString(new InputStreamReader(zipIn, "UTF-8")).trim();
}
if (syncInfo != null && csConfig != null) {
break;
}
}
}
finally {
zipIn.close();
}
}
// Developer plugin folder
else {
File syncInfoFile = DesignDirectory.getDesignDefinitionFile(file);
if (syncInfoFile.exists()) {
syncInfo = DesignDefinition.load(syncInfoFile);
}
File csConfigFile = new File(file, SystemContainerManager.CSCONFIG_PATH);
if (csConfigFile.exists()) {
csConfig = CSConfig.load(csConfigFile);
}
File licenseTextFile = new File(file, SystemContainerManager.LICENSE_PATH);
if (licenseTextFile.exists()) {
Reader reader = new InputStreamReader(new FileInputStream(licenseTextFile) , "UTF-8");
licenseText = WGUtils.readString(reader).trim();
reader.close();
}
}
if (syncInfo != null && csConfig != null && csConfig.getPluginConfig() != null) {
return new Configuration(syncInfo, csConfig, licenseText);
}
else {
return null;
}
}
public String getDesignURL() throws FileSystemException {
if (getPluginFile().isFile()) {
return "zip:file:///" + getPluginFile().getAbsolutePath();
}
else {
return getPluginFile().getAbsolutePath();
}
}
public PluginID getPluginID() {
return _pluginID;
}
public boolean isActive() {
return _active;
}
public void setActive(boolean active) {
_active = active;
}
public void checkDependencies() {
Iterator depsIt = _config.getCsConfig().getPluginConfig().getDependencies().iterator();
while (depsIt.hasNext()) {
PluginID depId = (PluginID) depsIt.next();
WGAPlugin depPlugin = (WGAPlugin) _parent.getActivePluginsByUniqueName().get(depId.getUniqueName());
if (depPlugin == null) {
addInstallationFault(new DependencyFault(depId, DependencyFault.ERROR_DEPENDENCY_NOT_AVAILABLE));
continue;
}
int versionCompare = depId.getVersion().compareTo(depPlugin.getPluginID().getVersion());
if (versionCompare > 0) {
addInstallationFault(new DependencyFault(depId, DependencyFault.ERROR_DEPENDENCY_WRONG_VERSION));
continue;
}
if (depPlugin.isActive() == false || depPlugin.isValid() == false) {
addInstallationFault(new DependencyFault(depId, DependencyFault.ERROR_DEPENDENCY_INACTIVE));
continue;
}
if (detectCircularReference(depPlugin)) {
addInstallationFault(new DependencyFault(depId, DependencyFault.ERROR_CIRCULAR_REFERENCE));
continue;
}
depPlugin.addDependentPlugin(this);
_mandatoryPlugins.put(depPlugin.getPluginID(), depPlugin);
}
}
private boolean detectCircularReference(WGAPlugin depPlugin) {
if (depPlugin.equals(this)) {
return true;
}
Iterator dependencies = depPlugin.getCsConfig().getPluginConfig().getDependencies().iterator();
while (dependencies.hasNext()) {
PluginID id = (PluginID) dependencies.next();
WGAPlugin plugin = getParent().getPluginByID(id);
if (plugin != null && detectCircularReference(plugin)) {
return true;
}
}
return false;
}
private void addDependentPlugin(WGAPlugin plugin) {
_dependentPlugins.put(plugin.getPluginID(), plugin);
}
public Map getMandatoryPlugins() {
return _mandatoryPlugins;
}
public boolean isValid() {
return _valid;
}
public void setValid(boolean valid) {
_valid = valid;
}
public WGAPluginSet getParent() {
return _parent;
}
public String getInstallationKey() {
return _installationKey;
}
public void setInstallationKey(String installationKey) {
_installationKey = installationKey;
}
public String buildDatabasePath() throws FileSystemException {
return new File(_parent.getPluginDBsDir(), getInstallationKey()).getPath();
}
public long getFileLastModified() {
return _fileLastModified;
}
public String getFilePath() {
return FilenameUtils.normalize(getParent().getCore().getWGAFile(_filePath).getAbsolutePath());
}
public int hashCode() {
final int PRIME = 31;
int result = 1;
result = PRIME * result + ((_pluginID == null) ? 0 : _pluginID.hashCode());
return result;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final WGAPlugin other = (WGAPlugin) obj;
if (_pluginID == null) {
if (other._pluginID != null)
return false;
}
else if (!_pluginID.equals(other._pluginID))
return false;
return true;
}
public List getInstallationFaults() {
return _installationFaults;
}
public void addInstallationFault(InstallationFault fault) {
_installationFaults.add(fault);
}
public String getIdentification() {
return getPluginID().toString();
}
public WorkspaceOperation getOperation() {
return _operation;
}
public void setOperation(WorkspaceOperation operation) {
_operation = operation;
}
public int getUpdateStatus() {
return _updateStatus;
}
public void setUpdateStatus(int updateStatus) {
_updateStatus = updateStatus;
}
public String buildDatabaseKey() {
return PluginConfig.PLUGIN_DBKEY_PREFIX + getInstallationKey();
}
public boolean isReconnectDatabase() {
return getRuntimeContext().isReconnect();
}
public void setReconnectDatabase(boolean reconnectDatabase) {
getRuntimeContext().setReconnect(reconnectDatabase);
}
public String getPluginHomepage() {
PluginConfig pc = getCsConfig().getPluginConfig();
if (!pc.isUsageAsContentStore()) {
return null;
}
String ph = pc.getPluginHomepage();
if (ph != null && !ph.trim().equals("")) {
return ph;
}
PublisherOption option = getCsConfig().findPublisherOption(WGACore.DBATTRIB_HOME_PAGE);
if (option != null) {
return option.getValue();
}
else {
return null;
}
}
protected void setFilePath(String filePath) {
_filePath = filePath;
}
public boolean isDirectory() {
return getPluginFile().isDirectory();
}
public Map getDependentPlugins() {
return _dependentPlugins;
}
public CSConfig getCsConfig() {
if (_config != null) {
return _config.getCsConfig();
}
else {
throw new IllegalStateException("The plugin has no valid configuration: " + _filePath);
}
}
public String getLicenseText() {
if (_config != null) {
return _config.getLicenseText();
}
else {
return null;
}
}
public DesignDefinition getSyncInfo() {
return _config.getSyncInfo();
}
public boolean isUpdated() {
return (getUpdateStatus() == UPDATESTATUS_UPDATE || getUpdateStatus() == UPDATESTATUS_UPDATE_IDENTICAL);
}
public RuntimeContext getRuntimeContext() {
return _parent.getRuntimeContext(this);
}
public String getRegisteredFilePath() {
return _filePath;
}
public boolean isDefaultPlugin() {
return _defaultPlugin;
}
public void setDefaultPlugin(boolean defaultPlugin) {
_defaultPlugin = defaultPlugin;
}
public boolean isPlatformPlugin() {
boolean isPlatformPlugin = (_filePath != null && _filePath.contains("${wga.defaultpluginsdir}"));
if (!isPlatformPlugin) {
isPlatformPlugin = PLATFORM_PLUGINS.contains(getPluginID().getUniqueName());
}
return isPlatformPlugin;
}
}