/*******************************************************************************
* 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/>.
******************************************************************************/
package de.innovationgate.wgpublisher;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
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.concurrent.ConcurrentHashMap;
import java.util.zip.ZipInputStream;
import org.apache.log4j.Logger;
import de.innovationgate.utils.DynamicClassLoadingChain;
import de.innovationgate.utils.WGUtils;
import de.innovationgate.webgate.api.WGACL;
import de.innovationgate.webgate.api.WGACLEntry;
import de.innovationgate.webgate.api.WGACLEntryFlags;
import de.innovationgate.webgate.api.WGAPIException;
import de.innovationgate.webgate.api.WGAuthorisationException;
import de.innovationgate.webgate.api.WGBackendException;
import de.innovationgate.webgate.api.WGCSSJSModule;
import de.innovationgate.webgate.api.WGDatabase;
import de.innovationgate.webgate.api.WGDatabaseConnectListener;
import de.innovationgate.webgate.api.WGDatabaseEvent;
import de.innovationgate.webgate.api.WGDatabaseEventListener;
import de.innovationgate.webgate.api.WGDesignChangeEvent;
import de.innovationgate.webgate.api.WGDesignChangeListener;
import de.innovationgate.webgate.api.WGDocument;
import de.innovationgate.webgate.api.WGFactory;
import de.innovationgate.webgate.api.WGFileContainer;
import de.innovationgate.webgate.api.WGHierarchicalDatabase;
import de.innovationgate.webgate.api.WGScriptModule;
import de.innovationgate.webgate.api.jdbc.FileContainer;
import de.innovationgate.webgate.api.schemadef.WGSchemaDefinition;
import de.innovationgate.webgate.api.utils.MasterSessionTask;
import de.innovationgate.wga.common.beans.csconfig.v1.ACLRole;
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.wga.common.beans.csconfig.v1.Version;
import de.innovationgate.wgpublisher.WGACore.DomainConfiguration;
import de.innovationgate.wgpublisher.design.db.DBDesignProvider;
import de.innovationgate.wgpublisher.design.fs.FileSystemDesignProvider;
import de.innovationgate.wgpublisher.expressions.ExpressionEngine;
import de.innovationgate.wgpublisher.expressions.ExpressionEngineFactory;
import de.innovationgate.wgpublisher.expressions.ExpressionResult;
import de.innovationgate.wgpublisher.hdb.HDBModel;
import de.innovationgate.wgpublisher.plugins.WGAPlugin;
import de.innovationgate.wgpublisher.plugins.WGAPluginSet;
import de.innovationgate.wgpublisher.webtml.utils.TMLContext;
public class SystemContainerManager implements WGDatabaseEventListener, WGDesignChangeListener, WGDatabaseConnectListener {
public static final String INITDUMP_CS = "init.wgacs";
public static final String INITDUMP_PLUGIN = "plugin-init.wgacs";
public static final String CSCONFIG_FILE = "csconfig.xml";
public static final String SCHEMA_FILE = "schema.xml";
public static final String CSCONFIG_PATH = "files/system/" + CSCONFIG_FILE;
public static final String SCHEMA_PATH = "files/system/" + SCHEMA_FILE;
public static final String LICENSE_PATH = "files/system/license.txt";
class SystemContainerContext {
private ContainerInfo _info;
private boolean _initACL;
private WGDatabase _db;
private boolean _initDisabled;
private WGAPlugin _plugin;
public SystemContainerContext(WGDatabase db, ContainerInfo info, WGAPlugin plugin, boolean initACL) {
_db = db;
_info = info;
_plugin = plugin;
_initACL = initACL;
_initDisabled = false;
if (_db.getDbReference().startsWith(PluginConfig.PLUGIN_DBKEY_PREFIX)) {
if (_info != null && _info.getCsConfig() != null && _info.getCsConfig().getPluginConfig() != null) {
PluginConfig pc = _info.getCsConfig().getPluginConfig();
if (pc instanceof de.innovationgate.wga.common.beans.csconfig.v3.PluginConfig) {
_initDisabled = ((de.innovationgate.wga.common.beans.csconfig.v3.PluginConfig) pc).isDisablePluginInit();
}
}
}
}
public void performInitialisation() {
performInitialisation(null);
}
public void performInitialisation(Boolean dbIsEmptyContent) {
try {
WGFileContainer fc = _db.getFileContainer("system");
// Build shortcuts so they are available in setup scripts
_core.buildDesignShortcuts(_db);
// Actions to perform on an db with empty acl (this will not be disabled with _initDisabled since even design provider plugins need general access)
if (_info != null && _info.getCsConfig() != null && _initACL) {
doEmptyACLActions(_db, _info.getCsConfig());
}
// Actions to bypass if initialisation is disabled
if (!_initDisabled) {
// Actions to perform on a content-empty db
boolean isEmptyContent = _db.isContentEmpty();
if (dbIsEmptyContent != null) {
isEmptyContent = dbIsEmptyContent.booleanValue();
}
// Process init dump
if (fc != null && isEmptyContent) {
String initDump;
if (_db.hasAttribute(WGACore.DBATTRIB_PLUGIN_VERSION) && fc.hasFile(INITDUMP_PLUGIN)) {
initDump = INITDUMP_PLUGIN;
}
else {
initDump = INITDUMP_CS;
}
if (fc.getFileNames().contains(initDump)) {
_log.info("Importing initial data for empty database '" + _db.getDbReference() + "'");
_core.importContentStoreDump(new ZipInputStream(fc.getFileData(initDump)), _db);
}
}
// We directly init HDB for this database if the system file container has a hdb model descriptor file
// So following scripts can use implicit model containers
if (fc != null && fc.hasFile(HDBModel.MODEL_FILE)) {
WGHierarchicalDatabase.getOrCreateInstance(_db);
}
// Process schema
if (_info != null && _info.getSchema() != null) {
_db.enforceSchema(_info.getSchema());
}
// Process init script
if (fc != null && isEmptyContent) {
if (_info.getCsConfig() != null && !WGUtils.isEmpty(_info.getCsConfig().getInitScript())) {
try {
WGCSSJSModule mod = _db.getCSSJSModule(_info.getCsConfig().getInitScript(), WGScriptModule.CODETYPE_TMLSCRIPT);
if (mod != null) {
_log.info("Running initialisation script of database '" + _db.getDbReference() + "'");
TMLContext context = new TMLContext(_db.getDummyContent(null), _core, null, null);
ExpressionResult result = ExpressionEngineFactory.getTMLScriptEngine().evaluateExpression(mod.getCode(), context, ExpressionEngine.TYPE_SCRIPT, null);
if (result.isError()) {
_log.error("Error running initialisation script '" + _info.getCsConfig().getInitScript() + "' of db '" + _db.getDbReference() + "'", result.getException());
}
}
else {
_log.error("Initialisiation script '" + _info.getCsConfig().getInitScript() + "' not found in database '" + _db.getDbReference() + "'");
}
}
catch (WGAPIException e) {
_log.error("Error initialising database '" + _db.getDbReference() + "'", e);
}
}
// Re-evaluate the default language
_db.onConnect(_core.new ValidateDefaultLanguageAction());
}
// Connection script
if (_info != null && _info.getCsConfig() != null) {
execConnectionScript(_db, _info.getCsConfig());
}
}
// Add as database event listener to get updates for csconfig.xml
if (_db.getDesignProvider() != null) {
_db.getDesignProvider().addDesignChangeListener(SystemContainerManager.this);
}
else {
_db.addDatabaseEventListener(SystemContainerManager.this);
}
}
catch (Exception e) {
_log.error("Error adding database for system container processing", e);
}
}
public void performDisconnection() {
try {
if (_db.isSessionOpen()) {
_db.reopenSession(null, null);
}
else {
_db.openSession();
}
// Disconnection script
if (_info != null && _info.getCsConfig() != null && _info.getCsConfig() instanceof de.innovationgate.wga.common.beans.csconfig.v3.CSConfig && !_initDisabled) {
de.innovationgate.wga.common.beans.csconfig.v3.CSConfig v3Config = (de.innovationgate.wga.common.beans.csconfig.v3.CSConfig) _info.getCsConfig();
if (!WGUtils.isEmpty(v3Config.getDisconnectionScript())) {
execDisconnectionScript(_db, v3Config);
}
}
}
catch (Exception e) {
_log.error("Error removing database from system container processing", e);
}
}
public void putPublisherOptions(Map<String, String> optionsTarget) {
if (_info == null || _info.getCsConfig() == null) {
return;
}
List publisherOptions = _info.getCsConfig().getPublisherOptions();
Iterator options = publisherOptions.iterator();
while (options.hasNext()) {
PublisherOption option = (PublisherOption) options.next();
optionsTarget.put(option.getName(), option.getValue());
}
}
}
class ContainerInfo {
private Date _lastModified;
private File _deploymentDir;
private boolean _deleteDeploymentDirOnFinalize = true;
private File _javaClassesDir = null;
private boolean _fromProviderDB = false;
private boolean _enfordedLibraryUpdate = false;
private CSConfig _csConfig = null;
private Set _enforcedMediaMappings = new HashSet();
private Set _enforcedElementMappings = new HashSet();
private Set _enforcedJobDefinitions = new HashSet();
private Set _enforcedEncoderMappings = new HashSet();
private Map<String,Date> _deployedJARDates = new HashMap<String,Date>();
private String _dbkey;
private WGSchemaDefinition _schema = null;
public ContainerInfo(WGFileContainer con, ContainerInfo oldInfo) throws WGAPIException, IOException, InvalidCSConfigVersionException {
// Determine if this system file container originates from another DB
// which means we can bypass many operations for this DB, since the provider DB already enforced them
WGDatabase db = con.getDatabase();
_dbkey = db.getDbReference();
if (db.getDesignProvider() != null && db.getDesignProvider() instanceof DBDesignProvider) {
_fromProviderDB = true;
}
// Create the deployment dir where to put JARs that are to be loaded to classpath
if (!_fromProviderDB) {
_deploymentDir = createDeploymentDir(con, oldInfo);
}
// Look for a Java directory in the design provider
if (db.getDesignProvider() instanceof FileSystemDesignProvider) {
FileSystemDesignProvider fsProvider = (FileSystemDesignProvider) db.getDesignProvider();
if (fsProvider.getJavaClassesPath() != null) {
_javaClassesDir = new File(fsProvider.getJavaClassesPath());
}
}
_lastModified = con.getLastModified();
if (con.getFileNames().contains(CSCONFIG_FILE)) {
InputStream in = con.getFileData(CSCONFIG_FILE);
_csConfig = CSConfig.load(in, true);
}
if (con.getFileNames().contains(SCHEMA_FILE)) {
InputStream in = con.getFileData(SCHEMA_FILE);
try {
_schema = WGSchemaDefinition.read(in);
}
catch (Exception e) {
_core.getLog().error("Exception reading schema definition of datababase " + _dbkey, e);
}
in.close();
}
}
public File getDeploymentDir() {
return _deploymentDir;
}
public Date getLastModified() {
return _lastModified;
}
protected void finalize() {
if (!isStaticClasspath() && _deploymentDir != null && _deploymentDir.exists() && _deleteDeploymentDirOnFinalize) {
WGUtils.delTree(_deploymentDir);
_deploymentDir = null;
}
}
public List<URL> getJARURLs() {
List<URL> jars = new ArrayList<URL>();
if (_deploymentDir != null) {
File[] files = _deploymentDir.listFiles();
if (files != null) {
for (int i = 0; i < files.length; i++) {
File file = files[i];
if (file.getName().endsWith(".jar")) {
try {
jars.add(file.toURL());
}
catch (MalformedURLException e) {
_log.error("Error creating URL for custom jar", e);
}
}
}
}
// Sort the URLs so this version can be compared to previous version
Collections.sort(jars, URLStringComparator.INSTANCE);
}
return jars;
}
public CSConfig getCsConfig() {
return _csConfig;
}
public Set getEnforcedElementMappings() {
return _enforcedElementMappings;
}
public Set getEnforcedEncoderMappings() {
return _enforcedEncoderMappings;
}
public Set getEnforcedJobDefinitions() {
return _enforcedJobDefinitions;
}
public Set getEnforcedMediaMappings() {
return _enforcedMediaMappings;
}
public void setCsConfig(CSConfig csConfig) {
_csConfig = csConfig;
}
public void setDeploymentDir(File deploymentDir) {
_deploymentDir = deploymentDir;
}
public void setLastModified(Date lastModified) {
_lastModified = lastModified;
}
public boolean isFromProviderDB() {
return _fromProviderDB;
}
public Collection<String> getJARDescriptions() {
if (_deploymentDir == null) {
return Collections.emptyList();
}
List<String> jars = new ArrayList<String>();
File[] files = _deploymentDir.listFiles();
if (files == null) {
return Collections.emptyList();
}
for (int i = 0; i < files.length; i++) {
File file = files[i];
if (file.getName().endsWith(".jar")) {
jars.add(_dbkey + " / " + file.getName());
}
}
if (getJavaClassesDir() != null) {
jars.add(_dbkey + " / java classes directory in design");
}
return jars;
}
public boolean isEnfordedLibraryUpdate() {
return _enfordedLibraryUpdate;
}
public void setEnfordedLibraryUpdate(boolean enfordedLibraryUpdate) {
_enfordedLibraryUpdate = enfordedLibraryUpdate;
}
public File getJavaClassesDir() {
return _javaClassesDir;
}
private File createDeploymentDir(WGFileContainer con, ContainerInfo oldInfo) throws IOException, WGAPIException {
// First collect jar files
List<String> jars = new ArrayList<String>();
Iterator fileNames = con.getFileNames().iterator();
while (fileNames.hasNext()) {
String fileName = (String) fileNames.next();
// Filter out jars
if (fileName.endsWith(".jar")) {
jars.add(fileName);
}
}
// If we have no jars we dont need a deployment dir
if (jars.size() == 0) {
return null;
}
// If we have on old ContainerInfo we can determine if anything has changed since last JAR deployment
if (oldInfo != null && jars.size() == oldInfo._deployedJARDates.size()) {
boolean keepDeployment = true;
// We only may recreate the deployment if the classpath is dynamic - If it is static we always keep it.
if (!isStaticClasspath()) {
Iterator<Map.Entry<String,Date>> oldJARs = oldInfo._deployedJARDates.entrySet().iterator();
while (oldJARs.hasNext()) {
Map.Entry<java.lang.String, java.util.Date> entry = (Map.Entry<java.lang.String, java.util.Date>) oldJARs.next();
if (!jars.contains(entry.getKey())) {
keepDeployment = false;
break;
}
if (!entry.getValue().equals(con.getFileLastModified(entry.getKey()))) {
keepDeployment = false;
break;
}
}
}
// If nothing changed we can take the old deployment dir and continue to use it (this will keep us from updating the lib loader)
if (keepDeployment) {
oldInfo._deleteDeploymentDirOnFinalize = false;
return oldInfo._deploymentDir;
}
}
// Create deployment dir for this container
File deploymentDir = File.createTempFile("fcd", ".tmp", _deploymentBase);
deploymentDir.delete();
deploymentDir.mkdir();
// Copy jars into it
Iterator<String> jarsIt = jars.iterator();
while (jarsIt.hasNext()) {
String jarName = jarsIt.next();
// Copy jar data to deployment dir
InputStream dataIn = con.getFileData(jarName);
OutputStream dataOut = new FileOutputStream(new File(deploymentDir, jarName));
WGUtils.inToOut(dataIn, dataOut, 2048);
dataIn.close();
dataOut.close();
_deployedJARDates.put(jarName, con.getFileLastModified(jarName));
}
return deploymentDir;
}
public String getDbkey() {
return _dbkey;
}
public boolean isStaticClasspath() {
if (_csConfig != null && _csConfig instanceof de.innovationgate.wga.common.beans.csconfig.v3.CSConfig) {
return ((de.innovationgate.wga.common.beans.csconfig.v3.CSConfig) _csConfig).isStaticClasspath();
}
else {
return false;
}
}
public WGSchemaDefinition getSchema() {
return _schema;
}
}
private WGACore _core;
private Map<String, ContainerInfo> _containerInfos = new ConcurrentHashMap<String, ContainerInfo>();
private File _deploymentBase;
private Logger _log = Logger.getLogger("wga.fcsystem");
public SystemContainerManager(WGACore core) {
_core = core;
_deploymentBase = new File(WGFactory.getTempDir(), "scdeployment");
_deploymentBase.mkdir();
}
public SystemContainerContext addDatabase(WGDatabase db, WGAPlugin plugin, boolean initACL) throws InvalidCSConfigVersionException {
if (!db.isDesignRole()) {
return null;
}
if (!db.isConnected()) {
db.addDatabaseConnectListener(this);
return null;
}
try {
// retrieve and set container info
ContainerInfo info = checkForUpdates(db);
// Validate design and database version
if (info != null && info.getCsConfig() != null && CSConfig.VERSIONCOMPLIANCE_WGA50.equals(info.getCsConfig().getVersionCompliance())) {
if (db.getContentStoreVersion() < WGDatabase.CSVERSION_WGA5) {
_log.error("Database " + db.getDbReference() + " is a content store of version " + db.getContentStoreVersion() + " while it's design needs a content store of version 5. The design may not work correctly.");
}
}
return new SystemContainerContext(db, info, plugin, initACL);
}
catch (InvalidCSConfigVersionException e) {
throw e;
}
catch (Exception e) {
_log.error("Error adding database for system container processing", e);
return null;
}
}
public SystemContainerContext addDatabase(WGDatabase db, boolean initACL) throws InvalidCSConfigVersionException {
return addDatabase(db, null, initACL);
}
private void execConnectionScript(WGDatabase db, CSConfig csConfig) {
String script = csConfig.getConnectionScript();
if (WGUtils.isEmpty(script)) {
return;
}
try {
WGCSSJSModule mod = db.getCSSJSModule(script, WGScriptModule.CODETYPE_TMLSCRIPT);
if (mod != null) {
_log.info("Running connection script of '" + db.getDbReference() + "'");
TMLContext context = new TMLContext(db.getDummyContent(null), _core, null, null);
ExpressionResult result = ExpressionEngineFactory.getTMLScriptEngine().evaluateExpression(mod.getCode(), context, ExpressionEngine.TYPE_SCRIPT, null);
if (result.isError()) {
_log.error("Error running connection script '" + csConfig.getConnectionScript() + "' of '" + db.getDbReference() + "'", result.getException());
}
}
else {
_log.error("Connection script '" + csConfig.getConnectionScript() + "' not found in design of '" + db.getDbReference() + "'");
}
}
catch (WGAPIException e) {
_log.error("Exception processing connection script of '" + db.getDbReference() + "'", e);
}
}
private void execDisconnectionScript(WGDatabase db, de.innovationgate.wga.common.beans.csconfig.v3.CSConfig csConfig) {
String script = csConfig.getDisconnectionScript();
if (WGUtils.isEmpty(script)) {
return;
}
try {
WGCSSJSModule mod = db.getCSSJSModule(script, WGScriptModule.CODETYPE_TMLSCRIPT);
if (mod != null) {
_log.info("Running disconnection script of '" + db.getDbReference() + "'");
TMLContext context = new TMLContext(db.getDummyContent(null), _core, null, null);
ExpressionResult result = ExpressionEngineFactory.getTMLScriptEngine().evaluateExpression(mod.getCode(), context, ExpressionEngine.TYPE_SCRIPT, null);
if (result.isError()) {
_log.error("Error running disconnection script '" + script + "' of '" + db.getDbReference() + "'", result.getException());
}
}
else {
_log.error("Disconnection script '" + script + "' not found in design of '" + db.getDbReference() + "'");
}
}
catch (WGAPIException e) {
_log.error("Exception processing disconnection script of '" + db.getDbReference() + "'", e);
}
}
private void doEmptyACLActions(WGDatabase db, CSConfig csConfig) throws WGAuthorisationException, WGBackendException, WGAPIException {
WGACL acl = db.getACL();
if (acl == null) {
_log.warn("Unable to retrieve ACL of database '" + db.getDbReference() + "' although database has ACL management feature");
return;
}
if (csConfig.getAnonymousAccessLevel() != -1) {
_log.info("Adding anonymous access level to ACL");
acl.createUserEntry(WGDatabase.ANONYMOUS_USER, csConfig.getAnonymousAccessLevel());
}
if (csConfig.getDefaultAccessLevel() != -1) {
_log.info("Adding default access level to ACL");
acl.createUserEntry("*", csConfig.getDefaultAccessLevel());
}
Iterator roles = csConfig.getRoles().iterator();
while (roles.hasNext()) {
ACLRole role = (ACLRole) roles.next();
_log.info("Adding role '" + role.getName() + "' to ACL");
acl.createRoleEntry(role.getName());
if (role.isDefaultManagerRole()) {
assingRoleToDefaultManager(db, acl, role);
}
}
}
private void assingRoleToDefaultManager(WGDatabase db, WGACL acl, ACLRole role) {
try {
// Retrieve default manager
DomainConfiguration domain = _core.getDomainConfigForDatabase(db);
if (domain == null || domain.getDefaultManager() == null) {
return;
}
// Retrieve ACL entry for default manager
WGACLEntry aclEntry = acl.getEntry(domain.getDefaultManager());
if (aclEntry == null) {
acl.createUserEntry(domain.getDefaultManager(), WGDatabase.ACCESSLEVEL_MANAGER);
}
// Modify flags to include role and store
WGACLEntryFlags flags = acl.parseFlags(aclEntry);
if (!flags.getRoles().contains(role.getName())) {
flags.getRoles().add(role.getName());
}
aclEntry.setFlags(flags.toString());
acl.save(aclEntry);
}
catch (WGAPIException e) {
_log.error("Error assigning role '" + role.getName() + "' to default manager for database '" + db.getDbReference() + "'", e);
}
}
public void removeDatabase(WGDatabase db) {
if (!db.isDesignRole()) {
return;
}
if (db.getDesignProvider() != null) {
db.getDesignProvider().removeDesignChangeListener(this);
}
else {
db.removeDatabaseEventListener(this);
}
ContainerInfo info = _containerInfos.remove(db.getDbReference());
if (info != null) {
WGAPlugin plugin = null;
PluginID pluginID = (PluginID) db.getAttribute(WGACore.DBATTRIB_PLUGIN_ID);
if (pluginID != null) {
plugin = _core.getPluginSet().getPluginByID(pluginID);
}
SystemContainerContext scContext = new SystemContainerContext(db, info, plugin, false);
scContext.performDisconnection();
_core.removeCSConfig(db, info, true);
}
}
public void databaseUpdate(WGDatabaseEvent event) {
try {
WGDocument doc = event.getEditedDocument();
if (doc != null) {
if (doc instanceof WGFileContainer) {
WGFileContainer con = (WGFileContainer) doc;
if (con.getName().equals("system")) {
checkForUpdates(event.getDatabase());
}
}
}
else {
checkForUpdates(event.getDatabase());
}
}
catch (Exception e) {
_log.error("Error checking for system container update", e);
}
}
protected ContainerInfo checkForUpdates(WGDatabase database) throws WGAPIException, IOException, InvalidCSConfigVersionException {
// Load container and old container info
WGFileContainer con = database.getFileContainer("system");
ContainerInfo info = _containerInfos.get(database.getDbReference());
// Double checked locking check for update need
if (needsUpdate(database, con, info)) {
synchronized (this) {
// Update container info when container exists
if (needsUpdate(database, con, info)) {
// Remove old info
if (info !=null) {
_core.removeCSConfig(database, info, false);
_containerInfos.remove(database.getDbReference());
}
if (con != null) {
// Create new info and store
info = new ContainerInfo(con, info);
_containerInfos.put(database.getDbReference(), info);
if (info.getCsConfig() != null) {
database.setAttribute(WGACore.DBATTRIB_CSCONFIG, info.getCsConfig());
}
else {
database.removeAttribute(WGACore.DBATTRIB_CSCONFIG);
}
// Process new info for complete WGA runtime
_core.enforceCSConfig(database, info);
// Process new info for specific database. We can only do this after the database has been fully connected
if (database.getAttribute(WGACore.DBATTRIB_FULLY_CONNECTED) != null) {
_core.updateFieldMappings(database, null);
}
}
}
}
}
return info;
}
private boolean needsUpdate(WGDatabase database, WGFileContainer con, ContainerInfo info) throws WGAPIException {
// Both dont exist
if (con == null && info == null) {
return false;
}
// No container but info exists: We must update to remove the info
else if (con == null && info != null) {
return true;
}
// Container exists but no info yet
else if (con != null && info == null) {
return true;
}
// Info and container exist: Compare change dates, return false if equal
else {
if (info.getLastModified().equals(con.getLastModified())) {
return false;
}
else {
return true;
}
}
}
public boolean isTemporary() {
return false;
}
public synchronized URL[] getJARURLs() {
List<URL> urls = new ArrayList<URL>();
Iterator<ContainerInfo> infos = _containerInfos.values().iterator();
while (infos.hasNext()) {
ContainerInfo info = infos.next();
urls.addAll(info.getJARURLs());
if (info.getJavaClassesDir() != null) {
try {
URL javaClassesURL = info.getJavaClassesDir().toURL();
if (!urls.contains(javaClassesURL)) {
urls.add(javaClassesURL);
}
}
catch (MalformedURLException e) {
_log.error("Exception registering java classes directory " + info.getJavaClassesDir().getPath(), e);
}
}
}
// Sort the URLs so this version can be compared to previous version
Collections.sort(urls, URLStringComparator.INSTANCE);
return urls.toArray(new URL[urls.size()]);
}
public List<String> getJARDescriptions() {
List<String> urls = new ArrayList<String>();
Iterator<ContainerInfo> infos = _containerInfos.values().iterator();
while (infos.hasNext()) {
ContainerInfo info = infos.next();
urls.addAll(info.getJARDescriptions());
}
return urls;
}
public void designChanged(WGDesignChangeEvent event) {
// In case of a design changed event by a design provider we must spawn
// the processing to a separate thread, bc. we cannot be sure that the
// consumer db that uses the provider was opened, yet we cannot open it
// in the same thread bc. this might be a security leak
MasterSessionTask task = new MasterSessionTask(event.getDatabase()) {
protected void exec(WGDatabase db) throws Throwable {
try {
checkForUpdates(db);
}
catch (Exception e) {
_log.error("Error checking for system container update", e);
}
}
};
task.run();
}
public void databaseConnected(WGDatabaseEvent event) {
try {
SystemContainerContext scc = addDatabase(event.getDatabase(), false);
if (scc != null) {
// Put publisher options from design now. Remove those that are reset as first level options
Map<String, String> publisherOptions = new HashMap<String, String>();
scc.putPublisherOptions(publisherOptions);
Set firstLevelPublisherOptions = (Set) event.getDatabase().getAttribute(WGACore.DBATTRIB_FIRSTLEVELPUBLISHEROPTIONS);
if (firstLevelPublisherOptions != null) {
Iterator flOptions = firstLevelPublisherOptions.iterator();
while (flOptions.hasNext()) {
String option = (String) flOptions.next();
publisherOptions.remove(option);
}
}
// Put publisher options. Convert to appropriate type if necessary
Iterator<String> optionKeys = publisherOptions.keySet().iterator();
while (optionKeys.hasNext()) {
String optionName = optionKeys.next();
String optionValue = publisherOptions.get(optionName);
event.getDatabase().setAttribute(optionName, optionValue);
}
scc.performInitialisation();
}
}
catch (InvalidCSConfigVersionException e) {
_log.error("The design of application '" + event.getDatabase().getDbReference() + "' was developed for a higher WGA version: " + e.getTargetVersion());
}
}
public void databaseConnectionError(WGDatabaseEvent event) {
}
public void updateLibraryLoader(DynamicClassLoadingChain libraryLoader) {
Iterator<ContainerInfo> infos = _containerInfos.values().iterator();
while (infos.hasNext()) {
ContainerInfo info = infos.next();
// Collect urls for this design
List<URL> urlList = info.getJARURLs();
if (info.getJavaClassesDir() != null) {
try {
URL javaClassesURL = info.getJavaClassesDir().toURL();
if (!urlList.contains(javaClassesURL)) {
urlList.add(javaClassesURL);
}
}
catch (MalformedURLException e) {
_log.error("Exception registering java classes directory " + info.getJavaClassesDir().getPath(), e);
}
}
if (urlList.size() == 0) {
continue;
}
// Look if it changed, if so, update the library loader with it
URL[] urls = urlList.toArray(new URL[urlList.size()]);
URL[] oldUrls = libraryLoader.getSubLoaderURLs(info.getDbkey());
if (oldUrls == null || !Arrays.equals(urls, oldUrls)) {
if (oldUrls == null || !info.isStaticClasspath()) {
try {
libraryLoader.updateSubLoader(info.getDbkey(), urls, info.isStaticClasspath());
_core.getLog().info("Updating WGA java library loader for design of application '" + info.getDbkey() + "' containing " + urls.length + " java libraries" + (info.isStaticClasspath() ? " (static classpath)" : ""));
}
catch (IllegalStateException e) {
_core.getLog().warn("Java libraries for application '" + info.getDbkey() + "' have changed but WGA java library loader will not be updated because the old db design was marked static");
}
}
else {
_core.getLog().warn("Java libraries for application '" + info.getDbkey() + "' have changed but WGA java library loader will not be updated because the db design is marked static");
}
}
}
}
}