de.innovationgate.wgpublisher.WGACore.java Source code

Java tutorial

Introduction

Here is the source code for de.innovationgate.wgpublisher.WGACore.java

Source

/*******************************************************************************
 * 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.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.lang.reflect.Constructor;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
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.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.prefs.Preferences;
import java.util.regex.PatternSyntaxException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import javax.activation.DataHandler;
import javax.crypto.SecretKey;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.jsp.PageContext;
import javax.xml.bind.JAXBException;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.httpclient.URI;
import org.apache.commons.httpclient.URIException;
import org.apache.commons.httpclient.util.URIUtil;
import org.apache.commons.vfs2.FileSystemException;
import org.apache.log4j.Appender;
import org.apache.log4j.AsyncAppender;
import org.apache.log4j.DailyRollingFileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.quartz.SchedulerException;
import org.quartz.impl.DirectSchedulerFactory;
import org.quartz.simpl.RAMJobStore;
import org.quartz.simpl.SimpleThreadPool;
import org.quartz.spi.JobStore;
import org.quartz.spi.ThreadPool;

import biz.minaret.log4j.DatedFileAppender;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.SingleValueConverter;
import com.thoughtworks.xstream.io.xml.Dom4JDriver;

import de.innovationgate.license.LicenseException;
import de.innovationgate.utils.Base64;
import de.innovationgate.utils.ClassLoaderProvider;
import de.innovationgate.utils.DESEncrypter;
import de.innovationgate.utils.DynamicClassLoadingChain;
import de.innovationgate.utils.FormattingException;
import de.innovationgate.utils.ObjectFormatter;
import de.innovationgate.utils.PatternListVerifier;
import de.innovationgate.utils.ReplaceProcessor;
import de.innovationgate.utils.TempFileInputStream;
import de.innovationgate.utils.TemporaryFile;
import de.innovationgate.utils.TransientObjectWrapper;
import de.innovationgate.utils.URLBuilder;
import de.innovationgate.utils.WGUtils;
import de.innovationgate.utils.XStreamUtils;
import de.innovationgate.utils.cache.Cache;
import de.innovationgate.utils.cache.CacheException;
import de.innovationgate.utils.cache.CacheFactory;
import de.innovationgate.utils.cache.EHCacheCore;
import de.innovationgate.utils.net.IPAddress;
import de.innovationgate.utils.net.IPRestriction;
import de.innovationgate.utils.net.IPs;
import de.innovationgate.utils.security.HashedPassword;
import de.innovationgate.utils.security.HashingException;
import de.innovationgate.utils.security.SHA1HashingScheme;
import de.innovationgate.utils.security.SymmetricEncryptionEngine;
import de.innovationgate.webgate.api.DefaultMimetypeDeterminationService;
import de.innovationgate.webgate.api.WGACL;
import de.innovationgate.webgate.api.WGAPIException;
import de.innovationgate.webgate.api.WGBackendException;
import de.innovationgate.webgate.api.WGContent;
import de.innovationgate.webgate.api.WGDatabase;
import de.innovationgate.webgate.api.WGDatabase.ConnectAction;
import de.innovationgate.webgate.api.WGDatabase.DatabaseAction;
import de.innovationgate.webgate.api.WGDatabaseConnectListener;
import de.innovationgate.webgate.api.WGDatabaseCore;
import de.innovationgate.webgate.api.WGDatabaseEvent;
import de.innovationgate.webgate.api.WGDesignProvider;
import de.innovationgate.webgate.api.WGDocument;
import de.innovationgate.webgate.api.WGException;
import de.innovationgate.webgate.api.WGFactory;
import de.innovationgate.webgate.api.WGFileAnnotator;
import de.innovationgate.webgate.api.WGFileConverter;
import de.innovationgate.webgate.api.WGHierarchicalDatabase;
import de.innovationgate.webgate.api.WGHierarchicalDatabaseCoreListener;
import de.innovationgate.webgate.api.WGIllegalArgumentException;
import de.innovationgate.webgate.api.WGLanguage;
import de.innovationgate.webgate.api.WGSystemException;
import de.innovationgate.webgate.api.WGUnavailableException;
import de.innovationgate.webgate.api.auth.AuthenticationException;
import de.innovationgate.webgate.api.auth.AuthenticationModule;
import de.innovationgate.webgate.api.auth.AuthenticationSession;
import de.innovationgate.webgate.api.auth.CertAuthCapableAuthModule;
import de.innovationgate.webgate.api.auth.FileAuthenticationModule;
import de.innovationgate.webgate.api.auth.RequestBasedAuthenticationModule;
import de.innovationgate.webgate.api.fake.WGFakeContentStore;
import de.innovationgate.webgate.api.jdbc.WGDatabaseImpl;
import de.innovationgate.webgate.api.modules.servers.DatabaseServerProperties;
import de.innovationgate.webgate.api.servers.WGDatabaseServer;
import de.innovationgate.webgate.api.utils.ContentStoreDumpManager;
import de.innovationgate.webgate.api.workflow.WGDefaultWorkflowEngine;
import de.innovationgate.wga.common.Constants;
import de.innovationgate.wga.common.LogLevel;
import de.innovationgate.wga.common.beans.csconfig.v1.CSConfig;
import de.innovationgate.wga.common.beans.csconfig.v1.ElementMapping;
import de.innovationgate.wga.common.beans.csconfig.v1.EncoderMapping;
import de.innovationgate.wga.common.beans.csconfig.v1.InvalidCSConfigVersionException;
import de.innovationgate.wga.common.beans.csconfig.v1.JobDefinition;
import de.innovationgate.wga.common.beans.csconfig.v1.MediaKey;
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.wga.common.beans.csconfig.v2.Shortcut;
import de.innovationgate.wga.config.Administrator;
import de.innovationgate.wga.config.ClientRestriction;
import de.innovationgate.wga.config.ClusterConfiguration;
import de.innovationgate.wga.config.ConfigValidationException;
import de.innovationgate.wga.config.ContentDatabase;
import de.innovationgate.wga.config.ContentStore;
import de.innovationgate.wga.config.Database;
import de.innovationgate.wga.config.DatabaseServer;
import de.innovationgate.wga.config.DesignConfiguration;
import de.innovationgate.wga.config.DesignReference;
import de.innovationgate.wga.config.Domain;
import de.innovationgate.wga.config.FieldMapping;
import de.innovationgate.wga.config.FilterMapping;
import de.innovationgate.wga.config.MigrationMessage;
import de.innovationgate.wga.config.MigrationResult;
import de.innovationgate.wga.config.PersonalisationConfiguration;
import de.innovationgate.wga.config.PersonalisationDatabase;
import de.innovationgate.wga.config.Share;
import de.innovationgate.wga.config.WGAConfiguration;
import de.innovationgate.wga.config.WGAConfigurationMigrator;
import de.innovationgate.wga.model.ValidationError;
import de.innovationgate.wga.model.VersionCompliance;
import de.innovationgate.wga.modules.ModuleDefinition;
import de.innovationgate.wga.modules.ModuleDependencyException;
import de.innovationgate.wga.modules.ModuleInstantiationException;
import de.innovationgate.wga.modules.ModuleRegistry;
import de.innovationgate.wga.modules.ModuleRegistryChangeListener;
import de.innovationgate.wga.modules.ModuleType;
import de.innovationgate.wga.modules.options.DefaultOptionDefinition;
import de.innovationgate.wga.modules.options.OptionConversionException;
import de.innovationgate.wga.modules.options.OptionDefinition;
import de.innovationgate.wga.modules.options.OptionReader;
import de.innovationgate.wga.modules.options.PasswordOptionEncoder;
import de.innovationgate.wga.modules.properties.DesignSourceProperties;
import de.innovationgate.wga.modules.types.AuthenticationSourceModuleType;
import de.innovationgate.wga.modules.types.ClusterServiceModuleType;
import de.innovationgate.wga.modules.types.ContentDatabaseModuleType;
import de.innovationgate.wga.modules.types.ContentDatabasePublisherOptionsModuleType;
import de.innovationgate.wga.modules.types.ContentStoreModuleType;
import de.innovationgate.wga.modules.types.ContentStorePublisherOptionsModuleType;
import de.innovationgate.wga.modules.types.DatabaseServerModuleType;
import de.innovationgate.wga.modules.types.DesignSourceModuleType;
import de.innovationgate.wga.modules.types.FileAnnotatorModuleType;
import de.innovationgate.wga.modules.types.FilterConfigModuleType;
import de.innovationgate.wga.modules.types.HTMLHeadInclusionModuleType;
import de.innovationgate.wga.modules.types.HashingSchemeType;
import de.innovationgate.wga.modules.types.LanguageBehaviourModuleType;
import de.innovationgate.wga.modules.types.PasswordEncodingType;
import de.innovationgate.wga.modules.types.PersonalisationDatabaseModuleType;
import de.innovationgate.wga.modules.types.SchedulerTaskModuleType;
import de.innovationgate.wga.modules.types.ShareModuleType;
import de.innovationgate.wga.modules.types.VirtualLinkResolverModuleType;
import de.innovationgate.wga.modules.types.WGAServerOptionsModuleType;
import de.innovationgate.wga.modules.types.WGAWebServiceModuleType;
import de.innovationgate.wga.modules.types.WebTMLElementModuleType;
import de.innovationgate.wga.modules.types.WebTMLEncoderModuleType;
import de.innovationgate.wga.modules.types.WorkflowEngineModuleType;
import de.innovationgate.wga.server.api.ObjectScope;
import de.innovationgate.wga.server.api.WGA;
import de.innovationgate.wgpublisher.DBLoginInfo.AuthType;
import de.innovationgate.wgpublisher.SystemContainerManager.ContainerInfo;
import de.innovationgate.wgpublisher.auth.BruteForceLoginBlocker;
import de.innovationgate.wgpublisher.auth.CSAuthModule;
import de.innovationgate.wgpublisher.auth.DelegatingAuthModule;
import de.innovationgate.wgpublisher.auth.DomainRedirectionAuthModule;
import de.innovationgate.wgpublisher.auth.WGAAuthModuleFactory;
import de.innovationgate.wgpublisher.cache.FileCache;
import de.innovationgate.wgpublisher.cache.PostprocessedResourcesCache;
import de.innovationgate.wgpublisher.cache.WebTMLCache;
import de.innovationgate.wgpublisher.cluster.ClusterService;
import de.innovationgate.wgpublisher.cluster.SingleNodeClusterService;
import de.innovationgate.wgpublisher.design.WGADesign;
import de.innovationgate.wgpublisher.design.WGADesignManager;
import de.innovationgate.wgpublisher.design.WGADesignRetrievalException;
import de.innovationgate.wgpublisher.design.db.DBDesignProvider;
import de.innovationgate.wgpublisher.design.fs.FileSystemDesignProvider;
import de.innovationgate.wgpublisher.design.sync.DesignFileValidator;
import de.innovationgate.wgpublisher.events.EventManager;
import de.innovationgate.wgpublisher.expressions.ExpressionEngineFactory;
import de.innovationgate.wgpublisher.expressions.tmlscript.IsolatedJARLoader;
import de.innovationgate.wgpublisher.expressions.tmlscript.RhinoExpressionEngine;
import de.innovationgate.wgpublisher.expressions.tmlscript.TMLScriptAppGlobalRegistry;
import de.innovationgate.wgpublisher.expressions.tmlscript.TMLScriptGlobal;
import de.innovationgate.wgpublisher.expressions.tmlscript.TMLScriptGlobalRegistry;
import de.innovationgate.wgpublisher.files.ImageFileConverter;
import de.innovationgate.wgpublisher.files.derivates.FileDerivateManager;
import de.innovationgate.wgpublisher.filter.WGAFilter;
import de.innovationgate.wgpublisher.filter.WGAFilterConfig;
import de.innovationgate.wgpublisher.hdb.HDBModel;
import de.innovationgate.wgpublisher.labels.WGAResourceBundleManager;
import de.innovationgate.wgpublisher.lang.DynamicLanguageBehaviour;
import de.innovationgate.wgpublisher.lang.InitializableLanguageBehaviour;
import de.innovationgate.wgpublisher.lang.LanguageBehaviour;
import de.innovationgate.wgpublisher.lang.OnlyDefaultLanguageBehaviour;
import de.innovationgate.wgpublisher.lang.StaticLanguageBehaviour;
import de.innovationgate.wgpublisher.log.AppLog;
import de.innovationgate.wgpublisher.log.AppLogAppender;
import de.innovationgate.wgpublisher.log.WGALoggerWrapper;
import de.innovationgate.wgpublisher.logserver.LogServer;
import de.innovationgate.wgpublisher.lucene.LuceneManager;
import de.innovationgate.wgpublisher.lucene.analysis.FileHandler;
import de.innovationgate.wgpublisher.mail.WGAMailConfiguration;
import de.innovationgate.wgpublisher.mail.WGAMailNotification;
import de.innovationgate.wgpublisher.mail.WGAMailService;
import de.innovationgate.wgpublisher.modules.poptions.ContentDatabasePublisherOptionsCollector;
import de.innovationgate.wgpublisher.modules.poptions.ContentStorePublisherOptionsCollector;
import de.innovationgate.wgpublisher.modules.serveroptions.CacheModuleDefinition;
import de.innovationgate.wgpublisher.modules.serveroptions.ServicesCollector;
import de.innovationgate.wgpublisher.modules.serveroptions.VariousOptionsCollector;
import de.innovationgate.wgpublisher.plugins.InvalidPluginException;
import de.innovationgate.wgpublisher.plugins.WGAPlugin;
import de.innovationgate.wgpublisher.plugins.WGAPlugin.Configuration;
import de.innovationgate.wgpublisher.plugins.WGAPluginSet;
import de.innovationgate.wgpublisher.problems.AccessLoggingScope;
import de.innovationgate.wgpublisher.problems.AdministrativeProblemType;
import de.innovationgate.wgpublisher.problems.ContentShareScope;
import de.innovationgate.wgpublisher.problems.DBServerScope;
import de.innovationgate.wgpublisher.problems.DatabaseScope;
import de.innovationgate.wgpublisher.problems.DomainScope;
import de.innovationgate.wgpublisher.problems.GlobalScope;
import de.innovationgate.wgpublisher.problems.JobScope;
import de.innovationgate.wgpublisher.problems.PluginScope;
import de.innovationgate.wgpublisher.problems.Problem;
import de.innovationgate.wgpublisher.problems.ProblemOccasion;
import de.innovationgate.wgpublisher.problems.ProblemRegistry;
import de.innovationgate.wgpublisher.problems.ProblemScope;
import de.innovationgate.wgpublisher.problems.ProblemSeverity;
import de.innovationgate.wgpublisher.problems.ProblemType;
import de.innovationgate.wgpublisher.scheduler.ConfigurationException;
import de.innovationgate.wgpublisher.scheduler.JavaTask;
import de.innovationgate.wgpublisher.scheduler.Job;
import de.innovationgate.wgpublisher.scheduler.JobFailedException;
import de.innovationgate.wgpublisher.scheduler.JobSchedule;
import de.innovationgate.wgpublisher.scheduler.OptimizeLuceneIndexTask;
import de.innovationgate.wgpublisher.scheduler.PendingReleaseTask;
import de.innovationgate.wgpublisher.scheduler.Scheduler;
import de.innovationgate.wgpublisher.scheduler.ScriptTask;
import de.innovationgate.wgpublisher.scheduler.Task;
import de.innovationgate.wgpublisher.sessions.AbstractWGAHttpSessionManager;
import de.innovationgate.wgpublisher.sessions.WGAHttpSessionListener;
import de.innovationgate.wgpublisher.shares.ShareDefinition;
import de.innovationgate.wgpublisher.shares.ShareInitException;
import de.innovationgate.wgpublisher.shares.ShareProperties;
import de.innovationgate.wgpublisher.so.NoopScopeObjectContextCreator;
import de.innovationgate.wgpublisher.so.ScopeObjectRegistry;
import de.innovationgate.wgpublisher.so.ScopeProvider;
import de.innovationgate.wgpublisher.test.TestCore;
import de.innovationgate.wgpublisher.url.DefaultURLBuilder;
import de.innovationgate.wgpublisher.url.RequestIndependentDefaultURLBuilder;
import de.innovationgate.wgpublisher.url.RequestIndependentURLBuilder;
import de.innovationgate.wgpublisher.url.TitlePathManager;
import de.innovationgate.wgpublisher.url.WGAURLBuilder;
import de.innovationgate.wgpublisher.vlink.ExternalVirtualLinkResolver;
import de.innovationgate.wgpublisher.vlink.VirtualLinkResolver;
import de.innovationgate.wgpublisher.vlink.VirtualLinkTarget;
import de.innovationgate.wgpublisher.websockets.IndependentWebSocketManager;
import de.innovationgate.wgpublisher.websockets.PageConnectionManager;
import de.innovationgate.wgpublisher.webtml.portlet.TMLPortletState;
import de.innovationgate.wgpublisher.webtml.utils.CRLFEncoder;
import de.innovationgate.wgpublisher.webtml.utils.FlagAwareFormatter;
import de.innovationgate.wgpublisher.webtml.utils.HTMLHeadInclusion;
import de.innovationgate.wgpublisher.webtml.utils.HTMLXMLEncodingFormatter;
import de.innovationgate.wgpublisher.webtml.utils.JSONEncodingFormatter;
import de.innovationgate.wgpublisher.webtml.utils.JavaScriptEncodingFormatter;
import de.innovationgate.wgpublisher.webtml.utils.NoneEncodingFormatter;
import de.innovationgate.wgpublisher.webtml.utils.PlainTextFormatter;
import de.innovationgate.wgpublisher.webtml.utils.TMLContext;
import de.innovationgate.wgpublisher.webtml.utils.TMLContextAwareFormatter;
import de.innovationgate.wgpublisher.webtml.utils.TMLException;
import de.innovationgate.wgpublisher.webtml.utils.TMLOption;
import de.innovationgate.wgpublisher.webtml.utils.TMLScriptHDBListenerFactory;
import de.innovationgate.wgpublisher.webtml.utils.URLEncodingFormatter;
import de.innovationgate.wgpublisher.webtml.utils.URLQueryEncodingFormatter;
import de.innovationgate.wgpublisher.webtml.utils.UniqueNamePartFormatter;

/**
 * Base class of the WGA runtime, containing any configuration, maintaing access
 * to any data source. Know what you are doing when you use this class!
 */
public class WGACore implements WGDatabaseConnectListener, ScopeProvider, ClassLoaderProvider {

    public static final WGACore INSTANCE = new WGACore();

    public static final String JOBNAME_PUBLISH_PENDING_RELEASE = "Publish contents pending release";
    public static final String JOBNAME_OPTIMIZE_LUCENE_INDEX = "Optimize Lucene Index";

    public static final List<Class<?>> DEFAULT_SERIALIZER_NONSERIALIZABLE_TYPES = Arrays.<Class<?>>asList(
            new Class[] { WGA.class, WGACore.class, WGDocument.class, WGDatabase.class, WGACL.class });

    public static final BitSet URLENCODER_PATH_PART_CHARACTERS = new BitSet(256);
    static {
        URLENCODER_PATH_PART_CHARACTERS.or(URI.allowed_within_path);
        URLENCODER_PATH_PART_CHARACTERS.clear('+');
        URLENCODER_PATH_PART_CHARACTERS.clear('%');
    }

    private WGFileConverter _fileConverter = new ImageFileConverter();

    public static class UpdateConfigOccasion implements ProblemOccasion {

        @Override
        public ProblemScope getDefaultScope() {
            return GlobalScope.INSTANCE;
        }

        @Override
        public Class<? extends ProblemType> getDefaultType() {
            return AdministrativeProblemType.class;
        }

        @Override
        public Class<?> getDefaultRefClass() {
            return WGACore.class;
        }

        @Override
        public boolean isClearedAutomatically() {
            return true;
        }

        @Override
        public int hashCode() {
            return getClass().hashCode();
        }

        @Override
        public boolean equals(Object obj) {
            return (obj instanceof UpdateConfigOccasion);
        }

    }

    public static class ConnectDatabaseProblemOccasion implements ProblemOccasion {

        private String _dbkey;
        private DatabaseScope _scope;

        public ConnectDatabaseProblemOccasion(String dbkey) {
            _dbkey = dbkey;
            _scope = new DatabaseScope(dbkey);
        }

        @Override
        public boolean isClearedAutomatically() {
            return true;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((_dbkey == null) ? 0 : _dbkey.hashCode());
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            ConnectDatabaseProblemOccasion other = (ConnectDatabaseProblemOccasion) obj;
            if (_dbkey == null) {
                if (other._dbkey != null)
                    return false;
            } else if (!_dbkey.equals(other._dbkey))
                return false;
            return true;
        }

        @Override
        public ProblemScope getDefaultScope() {
            return _scope;
        }

        @Override
        public Class<? extends ProblemType> getDefaultType() {
            return AdministrativeProblemType.class;
        }

        @Override
        public Class<?> getDefaultRefClass() {
            return WGACore.class;
        }

    }

    public class URLEncoder {

        public String encodeQueryPart(String str) throws URIException {

            if (str != null) {
                return URIUtil.encodeWithinQuery(str, getCharacterEncoding());
            } else {
                return null;
            }

        }

        public String encodePathPart(String str) throws URIException {

            if (str != null) {
                return URIUtil.encode(str, URLENCODER_PATH_PART_CHARACTERS, getCharacterEncoding());
            } else {
                return null;
            }

        }

        public String encodePath(String str) throws URIException {

            List<String> pathElements = new ArrayList<String>();
            for (String elem : str.split("/")) {
                pathElements.add(encodePathPart(elem));
            }
            return WGUtils.serializeCollection(pathElements, "/");

        }

        public String decode(String str) throws URIException {

            if (str != null) {
                try {
                    return WGUtils.decodeURI(str, getCharacterEncoding());
                } catch (UnsupportedEncodingException e) {
                    throw new URIException("Unsupported encoding: " + e.getMessage());
                } catch (MalformedURLException e) {
                    throw new URIException("Malformed URL: " + e.getMessage());
                }
            } else {
                return null;
            }

        }

    }

    private URLEncoder _urlEncoder = new URLEncoder();

    public URLEncoder getURLEncoder() {
        return _urlEncoder;
    }

    public interface OptionFetcher {
        Map<String, String> fetch(WGAConfiguration config);
    }

    /**
     * Map to store session logins, either transient or persistent depending on the serializability of their credentials
     * The map may return null for values that were created transiently on another cluster node.
     */
    public static class SessionLoginMap implements Map<Object, DBLoginInfo>, Serializable {

        private static final long serialVersionUID = 1L;

        private Map<Object, TransientObjectWrapper<DBLoginInfo>> _map = new HashMap<Object, TransientObjectWrapper<DBLoginInfo>>();

        @Override
        public DBLoginInfo put(Object key, DBLoginInfo value) {
            TransientObjectWrapper<DBLoginInfo> wrapper = wrap(value);

            DBLoginInfo oldValue = get(key);
            if (oldValue != null) {

                if (oldValue.equals(value)) {
                    return oldValue;
                }

                if (oldValue.getAccessFilter() != null && value.getAccessFilter() == null) {
                    value.setAccessFilter(oldValue.getAccessFilter());
                }
                for (Map.Entry<String, String> dbFilter : oldValue.getDbAccessFilters().entrySet()) {
                    if (!value.getDbAccessFilters().containsKey(dbFilter.getKey())) {
                        value.getDbAccessFilters().put(dbFilter.getKey(), dbFilter.getValue());
                    }
                }

            }

            return unwrap(_map.put(key, wrapper));
        }

        private TransientObjectWrapper<DBLoginInfo> wrap(DBLoginInfo value) {
            TransientObjectWrapper<DBLoginInfo> wrapper;
            if (value.getCredentials() == null || value.getCredentials() instanceof Serializable) {
                wrapper = new TransientObjectWrapper<DBLoginInfo>(false);
            } else {
                wrapper = new TransientObjectWrapper<DBLoginInfo>(true);
            }
            wrapper.set(value);
            return wrapper;
        }

        private DBLoginInfo unwrap(TransientObjectWrapper<DBLoginInfo> wrapper) {
            if (wrapper == null) {
                return null;
            } else {
                return wrapper.get();
            }
        }

        @Override
        public void clear() {
            _map.clear();
        }

        @Override
        public boolean containsKey(Object arg0) {
            return _map.containsKey(arg0);
        }

        @Override
        public boolean containsValue(Object arg0) {
            for (TransientObjectWrapper<DBLoginInfo> wrapper : _map.values()) {
                if (arg0.equals(wrapper.get())) {
                    return true;
                }
            }
            return false;
        }

        @Override
        public Set<java.util.Map.Entry<Object, DBLoginInfo>> entrySet() {

            Set<java.util.Map.Entry<Object, DBLoginInfo>> set = new HashSet<Map.Entry<Object, DBLoginInfo>>();
            for (final Map.Entry<Object, TransientObjectWrapper<DBLoginInfo>> entry : _map.entrySet()) {
                set.add(new Map.Entry<Object, DBLoginInfo>() {

                    @Override
                    public Object getKey() {
                        return entry.getKey();
                    }

                    @Override
                    public DBLoginInfo getValue() {
                        return unwrap(entry.getValue());
                    }

                    @Override
                    public DBLoginInfo setValue(DBLoginInfo value) {
                        return unwrap(entry.setValue(wrap(value)));
                    }

                });
            }
            return set;
        }

        @Override
        public DBLoginInfo get(Object arg0) {
            return unwrap(_map.get(arg0));
        }

        @Override
        public boolean isEmpty() {
            return _map.isEmpty();
        }

        @Override
        public Set<Object> keySet() {
            return _map.keySet();
        }

        @Override
        public void putAll(Map<? extends Object, ? extends DBLoginInfo> map) {
            for (Object key : map.keySet()) {
                put(key, map.get(key));
            }
        }

        @Override
        public DBLoginInfo remove(Object arg0) {
            return unwrap(_map.remove(arg0));
        }

        @Override
        public int size() {
            return _map.size();
        }

        @Override
        public Collection<DBLoginInfo> values() {

            List<DBLoginInfo> values = new ArrayList<DBLoginInfo>();
            for (TransientObjectWrapper<DBLoginInfo> wrapper : _map.values()) {
                values.add(wrapper.get());
            }
            return values;

        }

    }

    public class ServletFilterUpdater implements ModuleRegistryChangeListener {

        @Override
        public void moduleRegistryChanged(ModuleRegistry registry, Class<? extends ModuleType> moduleType) {

            initReadFilterMappings();
            if (getFilter() != null) {
                getFilter().initFilterChain();
            }

        }

    }

    public class FileAnnotatorUpdater implements ModuleRegistryChangeListener {

        @Override
        public void moduleRegistryChanged(ModuleRegistry registry, Class<? extends ModuleType> moduleType) {

            try {
                for (WGDatabase db : getContentdbs().values()) {
                    updateFileAnnotators(db);
                }
            } catch (Throwable e) {
                getLog().error("Exception updating file annotators", e);
            }

        }

    }

    public class HTMLHeadInclusionUpdater extends WGACore implements ModuleRegistryChangeListener {

        @Override
        public void moduleRegistryChanged(ModuleRegistry registry, Class<? extends ModuleType> moduleType) {

            List<HTMLHeadInclusion> htmlHeadInclusions = new ArrayList<HTMLHeadInclusion>();
            for (ModuleDefinition def : registry.getModulesForType(HTMLHeadInclusionModuleType.class).values()) {
                try {
                    HTMLHeadInclusion inc = (HTMLHeadInclusion) registry.instantiate(def);
                    htmlHeadInclusions.add(inc);
                } catch (Throwable e) {
                    getLog().error("Exception processing HTML head inclusion " + def.getTitle(Locale.getDefault()),
                            e);
                }
            }
            _htmlHeadInclusions = htmlHeadInclusions;

        }

    }

    public static final String EXTERNAL_PERSDBS_FOLDER = "#persdbs";

    public class WGAPublisherOptionReader implements ModuleRegistryChangeListener {

        private Map<String, OptionDefinition> _optionDefCache = new ConcurrentHashMap<String, OptionDefinition>();

        public Object readPublisherOptionOrDefault(WGDatabase db, String name) {

            Class typeClass = ContentDatabasePublisherOptionsModuleType.class;
            Class collectorClass = ContentDatabasePublisherOptionsCollector.class;
            if (db.hasFeature(WGDatabase.FEATURE_FULLCONTENTFEATURES)) {
                typeClass = ContentStorePublisherOptionsModuleType.class;
                collectorClass = ContentStorePublisherOptionsCollector.class;
            }
            String cacheKey = typeClass.getName() + ":" + name;

            // Fetch option definition
            OptionDefinition optionDef = _optionDefCache.get(cacheKey);
            if (optionDef == null) {

                ModuleDefinition pOptionDefs = WGACore.this.getModuleRegistry().getModuleDefinition(typeClass,
                        collectorClass);
                optionDef = pOptionDefs.getOptionDefinitions().get(name);

                if (optionDef == null) {
                    optionDef = new DefaultOptionDefinition(name);
                }

                _optionDefCache.put(cacheKey, optionDef);

            }

            // Read the option value, default it if neccessary
            Object value = db.getAttribute(name);
            if (value == null && optionDef.getDefaultValue() != null) {
                value = optionDef.getDefaultValue();
            }

            try {
                return OptionReader.unconvertOptionValue(optionDef, value);
            } catch (Exception e) {
                throw new RuntimeException("Exception unconverting publisher option '" + name + "' with value '"
                        + value + "' on database '" + db.getDbReference() + "'", e);
            }

        }

        @Override
        public void moduleRegistryChanged(ModuleRegistry registry, Class<? extends ModuleType> moduleType) {
            _optionDefCache.clear();
        }

    }

    /**
     * A reader for WGA configuration options, that always takes the current WGA configuration and caches once read option values
     */
    public class WGAConfigurationOptionReader {

        private OptionFetcher _optionFetcher = null;
        private Class<? extends ModuleType> _typeClass;
        private Class<?> _implClass;

        protected WGAConfigurationOptionReader(OptionFetcher optionFetcher, Class<? extends ModuleType> typeClass,
                Class<?> implClass) {
            _optionFetcher = optionFetcher;
            _typeClass = typeClass;
            _implClass = implClass;

            Map<String, String> target = _optionFetcher.fetch(getWgaConfiguration());
            if (target == null) {
                throw new IllegalArgumentException("Option fetcher does return options map");
            }

        }

        public Object readOptionValueOrDefault(String optionName) {
            Map<String, String> options = _optionFetcher.fetch(getWgaConfiguration());
            ModuleDefinition modDef = getModuleRegistry().getModuleDefinition(_typeClass, _implClass);
            if (modDef == null) {
                getLog().error(
                        "Cannot read module definition " + _typeClass.getName() + "/" + _implClass.getName());
                return null;
            }

            try {
                return getWgaConfiguration().getCachingOptionReader().readOptionValueOrDefault(options, optionName,
                        modDef);
            } catch (OptionConversionException e) {
                getLog().error("Exception reading server option '" + optionName + "'. Falling back to default", e);
                OptionDefinition optDef = modDef.getOptionDefinitions().get(optionName);
                if (optDef != null) {
                    try {
                        return OptionReader.unconvertOptionValue(optDef, optDef.getDefaultValue());
                    } catch (OptionConversionException e1) {
                        getLog().error("Exception unconverting default value of server option '" + optionName
                                + "'. Falling back to null", e);
                    }
                }

                return null;

            }
        }

    }

    public WGAConfigurationOptionReader getConfigOptionReader(OptionFetcher fetcher,
            Class<? extends ModuleType> typeClass, Class<?> implClass) {
        return new WGAConfigurationOptionReader(fetcher, typeClass, implClass);
    }

    public class WGAMimetypeDeterminationService extends DefaultMimetypeDeterminationService {

        public String determineByFilename(String fileName) {
            String mimeType = getServletContext().getMimeType(fileName);
            if (mimeType == null) {
                mimeType = super.determineByFilename(fileName);
            }
            return mimeType;
        }

    }

    public class VariableReplaceProcessor implements ReplaceProcessor {

        public int replace(String text, int from, int to, Writer out) throws IOException {

            // Isolate the variable
            int varEnd = text.indexOf("}", from);
            if (varEnd == -1) {
                out.write(text.substring(from, to + 1));
                return to + 1;
            }

            // Get the variable name
            String varName = text.substring(from + 2, varEnd).trim();

            // Perform replacements
            if (varName.equals("wga.cfgdir")) {
                out.write(getConfigFile().getParentFile().getAbsolutePath());
            } else if (varName.equals("wga.datadir")) {
                out.write(getWgaDataDir().getAbsolutePath());
            } else if (varName.equals("wga.devpluginsdir")) {
                String devPluginsPath = getDeveloperPluginsPath();
                if (devPluginsPath != null) {
                    out.write(devPluginsPath);
                } else {
                    out.write(getConfigFilePath());
                }
            } else if (varName.equals("wga.defaultpluginsdir")) {
                out.write(getServletContext().getRealPath("/WEB-INF/default-plugins"));
            }

            else if (varName.startsWith("sys.")) {
                out.write(System.getProperty(varName.substring(5)));
            } else if (varName.startsWith("env.")) {
                out.write(System.getenv(varName.substring(5)));
            }

            return varEnd + 1;

        }

    }

    public final VariableReplaceProcessor _replaceProcessorInstance = new VariableReplaceProcessor();

    /**
     * Domain name used for administrative logins
     */
    public static final String DOMAIN_ADMINLOGINS = "$adminlogins";

    public static final String ENCODER_CRLF = "crlf";

    public static final String ENCODER_RTFSYSTEM = "rtfsystem";

    public static final String ENCODER_RTF = "rtf";

    public static final String ENCODER_PLAINTEXT = "plaintext";

    private static final String ENCODER_XML = "xml";

    public static final String ENCODER_HTML = "html";

    public static final String ENCODER_URL = "url";
    public static final String ENCODER_URLQUERY = "urlquery";

    public static final String ENCODER_JAVASCRIPT = "javascript";

    public static final String ENCODER_JSON = "json";

    public static final String ENCODER_NAMEPART = "np";

    public static final String SYSPROPERTY_UNITTEST = "de.innovationgate.wga.unittest";

    public static final String SYSPROPERTY_UNITTEST_LOGDIR = "de.innovationgate.wga.unittest.logdir";

    public static final String SYSPROPERTY_JDBC_HOTPATCHES = "de.innovationgate.wga.jdbc.hotpatches";

    public static final String SYSPROPERTY_DEVELOPMENT_MODE = "de.innovationgate.license.DevelopmentModeEnabled";

    public static final String DBATTRIB_DEFAULTPORT_BASE = "DefaultPort";

    public static final String DBATTRIB_DEFAULTPORT_HTTP = "DefaultPortHTTP";

    public static final String DBATTRIB_DEFAULTPORT_HTTPS = "DefaultPortHTTPS";

    public static final PatternLayout LAYOUT_APPLOG = new PatternLayout("%d{dd.MM.yyyy HH:mm:ss} %p %m\n");

    public static final String SYSPROPERTY_DEFAULT_PLUGINS = "de.innovationgate.wga.defaultplugins";

    public static final String SYSPROPERTY_DEVELOPER_PLUGINS = "de.innovationgate.wga.devplugins";

    public static final String SYSPROPERTY_LOGPATH = "de.innovationgate.wga.logpath";

    public static final String DBATTRIB_PUBLISH_CONTENTFILES_WITH_DESIGNENCODING = "PublishContentFilesWithDesignEncoding";

    public static final boolean DBATTRIBDEFAULT_PUBLISH_CONTENTFILES_WITH_DESIGNENCODING = true;

    /*
     * Server options 
     */
    public static final String SERVEROPTION_SERVER_SCALINGTHRESHOLD = "Server.ScalingThreshold";

    public static final String SERVEROPTION_WEBTML_FILEUPLAD_MAXSIZE = "WebTML.FileUpload.MaxSize";

    public static final String SERVEROPTION_WEBTML_DIRECT_OUTPUT = "WebTML.DirectOutput";

    public static final String SERVEROPTION_SERVER_TESTSESSIONVARSERIALIZABLE = "Server.TestSessionVarSerializable";

    public static final String SERVEROPTION_SERVER_SESSIONTIMEOUT = "Server.SessionTimeout";
    public static final Integer SERVEROPTIONDEFAULT_SESSIONTIMEOUT = 60;

    /**
     * Server option for redirecting Logins to a specific protocol
     */
    public static final String SERVEROPTION_LOGINREDIRECTPROTOCOL = "Server.LoginRedirect.Protocol";

    /**
     * Server option for redirecting Logins to a specific host
     */
    public static final String SERVEROPTION_LOGINREDIRECTHOST = "Server.LoginRedirect.Host";

    /**
     * Server option for redirecting Logins to a specific port
     */
    public static final String SERVEROPTION_LOGINREDIRECTPORT = "Server.LoginRedirect.Port";

    /**
     * Server option determining the maximum size of the thread pool executing asynchronous events
     */
    public static final String SERVEROPTION_EVENTMANAGER_THREADPOOLSIZE = "Server.EventManager.ThreadPoolSize";

    public static final String SERVEROPTION_WEBSOCKETS_SESSION_WORKAROUND = "Server.WebSockets.SessionWorkaround";

    public static final String DBATTRIB_DIRECTACCESSDEFAULT = "DirectAccessDefault";

    /**
     * Publisher option defining the encoding of design resources - determines
     * the encoding in which file base resources are read from filesystem -
     * determines the source encoding for textual files within file containers
     */
    public static final String DBATTRIB_DESIGN_ENCODING = "DesignEncoding";

    /**
     * Publisher option for redirecting URLs to this DB to a different protocol
     */
    public static final String DBATTRIB_REDIRECTPROTOCOL = "RedirectProtocol";

    /**
     * Publisher option for redirecting URLs to this DB to a different host
     */
    public static final String DBATTRIB_REDIRECTHOST = "RedirectHost";

    /**
     * Publisher option for redirecting URLs to this DB to a different port
     */
    public static final String DBATTRIB_REDIRECTPORT = "RedirectPort";

    public static final String DBATTRIB_HDB_USE_VERSIONING = WGHierarchicalDatabase.DBATTRIB_HDB_USE_VERSIONING;

    public static final String SYSTEMLABEL_BASE_PATH = "de.innovationgate.wgpublisher.labels.";

    public static final String SERVEROPTION_DEFAULT_AUTHORING_APP = "DefaultAuthoringApp";

    public class ValidateDefaultLanguageAction implements ConnectAction {

        public void run(WGDatabase db) throws Exception {

            // Look if it is defined
            WGLanguage defaultLanguage = db.getLanguage(db.getDefaultLanguage());
            if (defaultLanguage == null || defaultLanguage.isDummy()) {
                if (db.getLanguages().size() > 0) {
                    //WGACore.this.log.warn("The default language '" + db.getDefaultLanguage() + "' is not defined in database " + db.getDbReference() + ". As other language definitions exist this may be misconfigured.");
                }
            }

            // Is there some root content in the default language? If so, we are satisfied.
            if (defaultLanguage != null) {
                WGContent content = db.getFirstReleasedContent(defaultLanguage.getName(), true);
                if (content != null) {
                    return;
                }
            }

            // No root content in default language? This may still be ok if there is no content at all. We try this with all languages.
            WGContent content = null;
            for (WGLanguage lang : db.getLanguages().values()) {
                content = db.getFirstReleasedContent(lang.getName(), true);
                if (content != null) {
                    break;
                }
            }
            if (content == null) {
                return;
            }

            // It seems there is content, but the currently selected default language is not available in any root doc.
            // Now we ask the db to determine a new default language
            db.determineDefaultLanguage();
            WGACore.this.log.info("Default language of database " + db.getDbReference() + " changed to '"
                    + db.getDefaultLanguage() + "'");

        }

    }

    public class UserAgentVerifier extends PatternListVerifier {

        public UserAgentVerifier(PersonalisationConfiguration config, WGACore core) {

            Iterator<String> agentExclusions = config.getPersonalisationAgentExclusions().iterator();
            while (agentExclusions.hasNext()) {
                String patternStr = agentExclusions.next();
                try {
                    addPattern(patternStr);
                } catch (PatternSyntaxException e) {
                    core.getLog().error("Cannot parse user agent exclusion as regular expression: " + patternStr);
                }
            }

        }

        public boolean isValidUserAgent(String userAgent) {

            // We accept no empty user agent, as it cannot be verified
            if (userAgent == null) {
                return false;
            }

            return (verify(userAgent) == null);
        }

    }

    public static final String ATTRIB_TMLDEBUG_TRACE_RESULTS = "tmlDebugTraceResults";

    public static final String ATTRIB_CURRENTTML = "currenttml";
    public static final String ATTRIB_CURRENTTMLDB = "currenttmldb";

    public static final String ATTRIB_AJAXINFO = "ajaxInfo";

    public static final String ATTRIB_REQUEST_CANCELLED = "RequestCancelled";

    private Scheduler _scheduler;

    private BruteForceLoginBlocker bruteForceLoginBlocker;

    public static final String ATTRIB_TMLDEBUG_DOCUMENTS = "tmlDebugDocuments";

    public static final String ATTRIB_TMLDEBUG = "tmlDebug";

    public static final String ATTRIB_TMLDEBUG_DISABLE_TMLSCRIPT_OPTIMIZATION = "tmlDebugDisableTMLScriptOptimization";

    public static final String ATTRIB_REQUESTTYPE = "RequestType";

    public static final String ATTRIB_EDITDOCUMENT = "editDocument";

    public static final String DBSESSIONCONTEXT_REQUEST = "Request";

    public static final String WGAMANAGER_URL = "wgamanager-4.0.jnlp";

    public static final String LANGUAGEBEHAVIOUR_DEFAULT = "default";
    public static final String LANGUAGEBEHAVIOUR_MAINCONTENT = "maincontent";
    public static final String LANGUAGEBEHAVIOUR_BROWSER = "browser";

    public static final String URL_PARAM_CLEAN = "$clean";

    private static DynamicClassLoadingChain libraryClassLoadingChain;

    private EventManager _eventManager;

    public static final String SESSION_COOKIESET = "CookieSet:";

    public static final String DBATTRIB_SESSIONCOOKIE = "SessionCookie";

    public static final String pubKey = "MIIBtzCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoDgYQAAoGAMfEHZxC9JGRuMSI9fuKewoha0wsywr6Yi/F6k+YERZKD8GIPdzpWqcD8kQ0FZqxaI7T+1xH0oRbnr1aVsyZSfRICuhWUcoJrThsuuYBFmjMtl8XDW9saa0VDQirxpH5ee3JqCHJurp6ik0XtqI25NBVLiJZQ1aJt6Znt4FPEosw=";

    private static final String SYSPROP_SKIP_LOCAL_ADMIN_LOGINS = "de.innovationgate.wga.skipLocalAdminLogins";

    private static final String SYSPROP_WARNINGS_ON_SERVER_CONSOLE = "de.innovationgate.wga.outputWarningsOnConsole";

    /**
     * checks if a login-retry is allowed - user can be redirected to login page
     * 
     * @param db
     * @param session
     * @return
     */
    public boolean allowLoginRetry(WGDatabase db, HttpSession session) {

        boolean dbCertAuth = db.certAuthEnabled();
        boolean domCertAuth = certAuthEnabledForDomain((String) db.getAttribute(WGACore.DBATTRIB_DOMAIN));

        if (!dbCertAuth && domCertAuth) {
            // we have a configuration conflict
            log.warn("The domain '" + db.getAttribute(WGACore.DBATTRIB_DOMAIN)
                    + "' is enabled for certificate authentication, but db '" + db.getDbReference()
                    + "' is not configured to support certificates. All dbs in a domain must support the same user credentials.");
        }

        if (dbCertAuth || domCertAuth) {
            // on certificate authentication a login retry makes no sense
            // user should not be redirected to login page
            return false;
        } else {
            DBLoginInfo loginInfo = getSessionLogins(session).get(db.getAttribute(WGACore.DBATTRIB_DOMAIN));
            return (loginInfo == null || loginInfo.isAnonymous());
        }

    }

    public String getDeveloperPluginsPath() {
        String customDefaultPlugins = System.getProperty(SYSPROPERTY_DEVELOPER_PLUGINS);
        if (customDefaultPlugins != null) {
            File pluginsFolder = getWGAFile(customDefaultPlugins);
            return pluginsFolder.getAbsolutePath();
        } else {
            return null;
        }
    }

    public boolean isAuthor(WGDatabase db, String ip, String type) {

        if (db == null) {
            return false;
        }

        if (!db.isSessionOpen()) {
            return false;
        }

        // Check db access rights
        if (db.getSessionContext().getAccessLevel() < WGDatabase.ACCESSLEVEL_AUTHOR) {
            return false;
        }

        return true;
    }

    public boolean isAdminLogin(String name, String password) {
        return isAdminLogin(name, password, null);
    }

    public boolean isAdminLogin(String name, String password, HttpServletRequest request) {
        // check if we should allow passwordless admin login from localhost
        if (request != null && isLocalRequest(request)
                && Boolean.parseBoolean(System.getProperty(SYSPROP_SKIP_LOCAL_ADMIN_LOGINS, "false"))) {
            return true;
        }

        if (name == null || password == null) {
            return false;
        }

        Administrator admin = (Administrator) _wgaConfiguration.getAdministrator(name);
        if (admin == null) {
            return false;
        }

        boolean loggedIn = getBruteForceLoginBlocker().login(admin, password);
        if (request != null) {
            if (!loggedIn) {
                log.warn("Failed admin login attempt for account '" + name + "' from '" + request.getRemoteAddr()
                        + "'.");
            }
        }
        return loggedIn;
    }

    public boolean doAdminLogin(String name, String password, HttpServletRequest request) {
        if (isAdminLogin(name, password, request)) {
            request.getSession().setAttribute(WGACore.SESSION_ADMINNAME, name);
            request.getSession().setAttribute(WGACore.SESSION_ADMINPASSWORD, password);
            return true;
        } else {
            request.getSession().removeAttribute(WGACore.SESSION_ADMINNAME);
            request.getSession().removeAttribute(WGACore.SESSION_ADMINPASSWORD);
            return false;
        }
    }

    public void doAdminLogout(HttpServletRequest request) {
        request.getSession().removeAttribute(WGACore.SESSION_ADMINNAME);
        request.getSession().removeAttribute(WGACore.SESSION_ADMINPASSWORD);
    }

    public String getElementClassForName(String name) throws TMLException {
        String className = this.systemElements.get(name.toLowerCase());
        if (className == null) {
            className = this.customElements.get(name.toLowerCase());
        }

        if (className == null) {
            ModuleDefinition modDef = getModuleRegistry().getModuleDefinitionByKey(WebTMLElementModuleType.class,
                    name.toLowerCase());
            if (modDef != null) {
                try {
                    modDef.testDependencies();
                } catch (ModuleDependencyException e) {
                    throw new TMLException("WebTML element '" + name + "' not available bc. of missing dependency: "
                            + e.getMessage(), true);
                }
                className = modDef.getImplementationClass().getName();
            }
        }

        return className;
    }

    public String getConfigFilePath() {
        try {
            return this.getConfigFile().getPath();
        } catch (Exception e) {
            this.log.error("Error retrieving config file path", e);
            return "(Error retrieving config file path)";
        }

    }

    public MediaKey getMediaKey(String key) {

        MediaKey mediaKey = this.systemMediaKeys.get(key);
        if (mediaKey == null) {
            mediaKey = this.customMediaKeys.get(key);
        }
        return mediaKey;
    }

    public Set<String> getMediaKeys() {

        Set<String> set = new HashSet<String>();
        set.addAll(systemMediaKeys.keySet());
        set.addAll(customMediaKeys.keySet());
        return set;

    }

    public boolean isDeploymentKeyDefined(String layoutKey) {
        return this._deployer.getLayoutMappings().containsKey(layoutKey);
    }

    public static SessionLoginMap getSessionLogins(javax.servlet.http.HttpSession session) {

        SessionLoginMap logins = (SessionLoginMap) session.getAttribute(WGPDispatcher.SESSION_LOGINS);
        if (logins == null) {
            logins = new SessionLoginMap();
            session.setAttribute(WGPDispatcher.SESSION_LOGINS, logins);
        }
        return logins;

    }

    public WGDatabase openContentDB(String key, javax.servlet.http.HttpServletRequest request) throws WGException {
        return openContentDB(key, request, false);
    }

    public WGDatabase openContentDB(String key, javax.servlet.http.HttpServletRequest request, boolean useMaster)
            throws WGException {

        WGDatabase db = this.contentdbs.get(key);
        if (db != null) {
            return openContentDB(db, request, useMaster);
        } else {
            return null;
        }

    }

    public WGDatabase openContentDB(WGDatabase db, javax.servlet.http.HttpServletRequest request, boolean useMaster)
            throws WGException {
        return openContentDB(db, request, (request != null ? request.getSession() : null), useMaster, null);
    }

    public WGDatabase openContentDB(WGDatabase db, HttpServletRequest request, HttpSession session,
            boolean useMaster, WGDatabase hintDB) throws WGException {

        // If already open, and useMaster==isMaster, just return
        if (db.isSessionOpen()) {
            if (db.getSessionContext().isMasterSession() == useMaster) {
                return db;
            }
        }

        // If db is not ready throw an exception
        if (!db.isReady()) {
            throw new WGUnavailableException(db);
        }

        // Force master login
        if (useMaster) {
            db.openSession();
            return prepareDB(db, request);
        }

        // check client access to this db
        if (request != null && !this.isClientPermitted(db, request)) {
            throw new ClientAccessException("Client '" + request.getRemoteAddr()
                    + "' is not permitted to access app '" + db.getDbReference() + "'.");
        }

        // Load config and login info from domain
        WGADomain domainConfig = getDomainForDatabase(db);
        DBLoginInfo sessionLoginInfo = null;
        String accessFilter = null;
        if (session != null) {
            sessionLoginInfo = WGACore.getSessionLogins(session).get(domainConfig.getName());
            if (sessionLoginInfo != null) {
                if (sessionLoginInfo.getDbAccessFilters().containsKey(db.getDbReference())) {
                    accessFilter = sessionLoginInfo.getDbAccessFilters().get(db.getDbReference());
                } else {
                    accessFilter = sessionLoginInfo.getAccessFilter();
                }
            }
        }

        // Check if regular or request based login is forced by some filter
        Boolean forceRegular = (request != null ? (Boolean) request.getAttribute(ATTRIB_FORCEREGULARLOGIN) : null);
        if (forceRegular == null) {
            forceRegular = Boolean.FALSE;
        }
        Boolean forceRequestBased = (request != null ? (Boolean) request.getAttribute(ATTRIB_FORCEREQUESTBASELOGIN)
                : null);
        if (forceRequestBased == null) {
            forceRequestBased = Boolean.FALSE;
        }

        // Load HTTP basic auth information
        DBLoginInfo httpLoginInfo = (request != null
                ? (DBLoginInfo) request.getAttribute(WGPRequestPath.REQATTRIB_HTTPLOGIN)
                : null);

        // Certificate based login
        if (request != null && !forceRegular.booleanValue() && db.certAuthEnabled()) {
            return openContentDBCertAuth(db, request, accessFilter);
        }

        // Requestinfo based login
        else if (request != null && (!forceRegular || forceRequestBased) && db.getAuthenticationModule() != null
                && db.getAuthenticationModule() instanceof RequestBasedAuthenticationModule) {
            return openContentDBRequestBased(db, request, accessFilter);
        }

        // HTTP basic authentication login
        else if (httpLoginInfo != null) {
            getBruteForceLoginBlocker().login(db, httpLoginInfo.getUserName(), httpLoginInfo.getCredentials(),
                    accessFilter);
            if (db.isSessionOpen() && session != null) {
                updateLoginInfo(db, request, DBLoginInfo.AuthType.PASSWORD);
            }
            return prepareDB(db, request);
        }

        // do standard login process
        else {

            // Try to login via session token
            if (request != null && db.hasFeature(WGDatabase.FEATURE_SESSIONTOKEN)) {
                String cookieName = (String) db.getAttribute(WGACore.DBATTRIB_SESSIONCOOKIE);
                if (cookieName != null) {
                    Cookie[] cookies = request.getCookies();
                    if (cookies != null) { // Can actually happen, especially
                                           // with
                                           // non-browser http clients
                        Cookie tokenCookie = null;
                        for (int idx = 0; idx < cookies.length; idx++) {
                            if (cookies[idx].getName().equals(cookieName)) {
                                tokenCookie = cookies[idx];
                                break;
                            }
                        }
                        if (tokenCookie != null) {
                            db.openSession(WGDatabase.SESSIONTOKEN_USER, tokenCookie.getValue(), accessFilter,
                                    request);
                            if (db.isSessionOpen()) {
                                // CONSIDERED HARMFUL: Session tokens may
                                // expire. Safer to always retrieve "fresh" from
                                // cookie
                                // loginInfo = new
                                // DBLoginInfo(WGDatabase.SESSIONTOKEN_USER,
                                // tokenCookie.getValue());
                                // this.getSessionLogins(session).put(domainConfig.getName(),
                                // loginInfo);
                                return prepareDB(db, request);
                            }
                        }
                    }
                }
            }

            // If no session available, log in by hint or anonymous
            if (session == null) {

                if (hintDB != null && hintDB.isSessionOpen()) {
                    if (hintDB.getSessionContext().isMasterSession()) {
                        db.openSession();
                    } else {
                        db.openSession(hintDB.getSessionContext().getUser(),
                                hintDB.getSessionContext().getPassword(), accessFilter, request);
                    }
                    if (db.isSessionOpen()) {
                        return prepareDB(db, request);
                    }

                }

                db.openSession(WGDatabase.ANONYMOUS_USER, null, null, request);
                return prepareDB(db, request);
            }

            // Try to login by previously stored domain-specific login
            if (sessionLoginInfo != null && !WGDatabase.ANONYMOUS_USER.equals(sessionLoginInfo.getUserName())) {
                int accessLevel = db.openSession(sessionLoginInfo.getUserName(), sessionLoginInfo.getCredentials(),
                        accessFilter, request);
                if (accessLevel > WGDatabase.ACCESSLEVEL_NOTLOGGEDIN) {
                    return prepareDB(db, request);
                } else {
                    if (domainConfig.getAuthModule() != null) {
                        getLog().warn("User '" + sessionLoginInfo.getUserName() + "' could not login to database '"
                                + db.getAttribute(DBATTRIB_DBKEY)
                                + "' although she/he could login to the domain authentication. Is the domain '"
                                + db.getAttribute(DBATTRIB_DOMAIN) + "' misconfigured?");
                    } else {
                        getLog().warn("User '" + sessionLoginInfo.getUserName() + "' could not login to database '"
                                + db.getAttribute(DBATTRIB_DBKEY)
                                + "' although another db in the same domain permitted it. Is the domain '"
                                + db.getAttribute(DBATTRIB_DOMAIN) + "' misconfigured?");
                    }
                    // Misconfigured domains will no longer result in dropped
                    // logins
                    // this.getSessionLogins(session).remove(domain);
                }
            }

            // Anonymous login, if nothing else applies. CANNOT BE STORED, bc.
            // Sessionlogins may suddenly be available without notice
            db.openSession(WGDatabase.ANONYMOUS_USER, null, accessFilter, request);
            return prepareDB(db, request);
        }
    }

    /**
     * opens a content db based upon request.getRemoteUser and request.getUserPrincipal()
     * if request.getRemoteUser is 'null' and request.getUserPrincipal()!=null, WGDatabase.UNKNOWN_REMOTE_USER is given to the authmodule
     * @param db
     * @param request
     * @return
     * @throws WGAPIException
     * @throws ClientAccessException 
     */
    private WGDatabase openContentDBRequestBased(WGDatabase db, HttpServletRequest request, String accessFilter)
            throws WGException {
        if (request == null) {
            return prepareDB(db, request);
        }

        String user = request.getRemoteUser();
        Principal credentials = request.getUserPrincipal();

        if (user == null) {
            user = credentials == null ? WGDatabase.ANONYMOUS_USER : WGDatabase.UNKNOWN_REMOTE_USER;
        }

        db.openSession(user, credentials, accessFilter, request);

        if (db.isSessionOpen()) {
            updateLoginInfo(db, request, DBLoginInfo.AuthType.REQUEST);
        }

        return prepareDB(db, request);
    }

    private WGDatabase openContentDBCertAuth(WGDatabase db, HttpServletRequest request, String accessFilter)
            throws WGException {

        // retrieve certificate chain
        X509Certificate certChain[] = (X509Certificate[]) request
                .getAttribute("javax.servlet.request.X509Certificate");
        if (certChain == null || certChain.length < 1) {
            getLog().warn("Failed login for client " + request.getRemoteAddr() + " to database "
                    + db.getDbReference() + ": No client certificate chain provided");
            return prepareDB(db, request);
        }

        // retrieve client cert
        X509Certificate clientCert = certChain[0];
        if (clientCert == null) {
            getLog().warn("Failed login for client " + request.getRemoteAddr() + " to database "
                    + db.getDbReference() + ": No client certificate provided");
            return prepareDB(db, request);
        }

        db.openSession(clientCert, accessFilter);
        if (db.isSessionOpen()) {
            updateLoginInfo(db, request, DBLoginInfo.AuthType.CERTIFICATE);
        }
        return prepareDB(db, request);
    }

    private void updateLoginInfo(WGDatabase db, HttpServletRequest request, AuthType authType) throws WGException {
        SessionLoginMap sessionLoginsMap = WGACore.getSessionLogins(request.getSession());
        DBLoginInfo oldLoginInfo = sessionLoginsMap.get(db.getAttribute(DBATTRIB_DOMAIN));
        DBLoginInfo loginInfo;
        if (oldLoginInfo != null) {
            loginInfo = new DBLoginInfo(db.getSessionContext().getUser(), db.getSessionContext().getCredentials(),
                    authType, oldLoginInfo.getAccessFilter(), oldLoginInfo.getDbAccessFilters());
        } else {
            loginInfo = new DBLoginInfo(db.getSessionContext().getUser(), db.getSessionContext().getCredentials(),
                    authType);
        }

        if (oldLoginInfo == null || !oldLoginInfo.equals(loginInfo)) {
            sessionLoginsMap.put(db.getAttribute(DBATTRIB_DOMAIN), loginInfo);
            if (db.hasFeature(WGDatabase.FEATURE_FULLCONTENTFEATURES)) {
                WGA.get(request, null, this).app(db).createEvent("auth=login")
                        .param("userName", loginInfo.getUserName())
                        .param("authType", DBLoginInfo.AuthType.CERTIFICATE).fireOnSession();
            }
        }
    }

    /**
     * checks if domain is enabled for certificate authentication per definition
     * a domain is enabled for certauth if - the authmodule configured on
     * domainlevel has certauth enabled - or one db in this domain has an
     * certauth-enabled authmodule
     * 
     * @param domainName
     * @return true/false
     */
    public boolean certAuthEnabledForDomain(String domainName) {

        WGADomain domain = getDomains(domainName);
        if (domain == null) {
            return false;
        }

        if (domain.getConfig().getAuthenticationSource() instanceof CertAuthCapableAuthModule) {
            CertAuthCapableAuthModule certAuthModule = (CertAuthCapableAuthModule) domain.getConfig()
                    .getAuthenticationSource();
            return certAuthModule.isCertAuthEnabled();
        }

        return false;

    }

    /**
     * returns a list with all dbs in the given domain
     * 
     * @param domain
     * @return
     */
    public List<WGDatabase> getDatabasesForDomain(String domain) {
        Iterator<WGDatabase> dbs = this.contentdbs.values().iterator();
        ArrayList<WGDatabase> domDbs = new ArrayList<WGDatabase>();
        while (dbs.hasNext()) {
            WGDatabase db = dbs.next();
            if (domain.equals(db.getAttribute(WGACore.DBATTRIB_DOMAIN))) {
                domDbs.add(db);
            }
        }
        return domDbs;
    }

    /**
     * returns all databases available within this request (for this user)
     * 
     * @param request
     * @return a list of all databases which can be accessed by this request
     */
    public List<WGDatabase> openContentDBs(javax.servlet.http.HttpServletRequest request) {
        ArrayList<WGDatabase> dbs = new ArrayList<WGDatabase>();
        Iterator<String> dbKeys = contentdbs.keySet().iterator();
        while (dbKeys.hasNext()) {
            String dbKey = dbKeys.next();
            WGDatabase db;
            try {
                db = openContentDB(dbKey, request);
                if (db.isSessionOpen()) {
                    dbs.add(db);
                }
            } catch (WGException e) {
                // ignore db
            }
        }
        return dbs;
    }

    /**
     * returns all databases available within this request (for this user) which
     * contains to domain
     * 
     * @param request
     * @param domain
     * @return a list of all databases which can be accessed by this request and
     *         contains to domain
     */
    public List<WGDatabase> openContentDBs(javax.servlet.http.HttpServletRequest request, String domain) {
        ArrayList<WGDatabase> dbs = new ArrayList<WGDatabase>();
        Iterator<String> dbKeys = contentdbs.keySet().iterator();
        while (dbKeys.hasNext()) {
            String dbKey = dbKeys.next();
            try {
                WGDatabase db = openContentDB(dbKey, request);
                if (db.isSessionOpen()) {
                    String dbDomain = (String) db.getAttribute(WGACore.DBATTRIB_DOMAIN);
                    if (dbDomain.equals(domain)) {
                        dbs.add(db);
                    }
                }
            } catch (WGException e) {
                // ignore db
            }
        }
        return dbs;
    }

    public WGDatabase prepareDB(WGDatabase db, HttpServletRequest request) {

        if (db.isSessionOpen()) {

            // Check for system file container update if the db's design provider does not notify
            if (db.getDesignProvider() != null && !db.getDesignProvider().isNotifying()) {
                try {
                    getSystemContainerManager().checkForUpdates(db);
                } catch (Exception e) {
                    getLog().error("Exception updating system file container", e);
                }
            }

            // Set some session context attributes
            if (request != null) {
                db.getSessionContext().setAttribute(WGACore.DBSESSIONCONTEXT_REQUEST, request);
                db.getSessionContext().setTask(
                        "WGA Server " + request.getMethod() + " Request: " + request.getRequestURL().toString());
            }
        }

        return db;
    }

    public WGDatabase openPersonalisationDB(String domain) throws WGAPIException {

        WGADomain domainCfg = getDomains(domain);
        if (domainCfg == null) {
            return null;
        }

        WGDatabase db = this.personalisationdbs.get(domainCfg.getUID());
        if (db == null) {
            return null;
        }

        if (!db.isSessionOpen()) {
            db.openSession();
        }

        return db;

    }

    private File licenseFile;

    private Map<String, Class> _customFormatters = Collections.synchronizedMap(new HashMap<String, Class>());
    private Map<String, Class> _systemFormatters = Collections.synchronizedMap(new HashMap<String, Class>());

    public boolean isMediaKeyDefined(String mediaKey) {
        return this.systemMediaKeys.containsKey(mediaKey) || this.customMediaKeys.containsKey(mediaKey);
    }

    // private String DBATTRIB_LOGGER = ATTRIB_BASE + "Logger";

    public void removePersonalisationDB(String domain) {

        WGDatabase db = this.personalisationdbs.remove(domain);
        if (db == null) {
            return;
        }

        // Notify all managed DB attributes
        Iterator attNames = db.getAttributeNames().iterator();
        while (attNames.hasNext()) {
            Object att = db.getAttribute((String) attNames.next());
            if (att instanceof ManagedDBAttribute) {
                ((ManagedDBAttribute) att).close();
            }

        }

        // Close the database
        try {
            db.close();
            this.log.info("Removed personalisation database for domain \"" + domain + "\"");
        } catch (WGAPIException e) {
            this.log.error("Unable to remove personalisation database for domain \"" + domain + "\"", e);
        }

    }

    // Tool threads
    private WGPDeployer _deployer;

    private ExternalFileServingConfig _externalFileServingConfig = new ExternalFileServingConfig();

    public ExternalFileServingConfig getExternalFileServingConfig() {
        return _externalFileServingConfig;
    }

    public synchronized void removeContentDB(String key) {

        WGDatabase db = this.contentdbs.remove(key);
        if (db != null) {

            String uuid = db.getUUID();
            if (uuid != null) {
                _contentdbUuidsToKeys.remove(uuid);
            }

            // Deregister from wgacore objects
            _systemContainerManager.removeDatabase(db);
            _deployer.removeLayouts(db);
            _eventManager.removeDatabaseEvents(key);

            db.removeDatabaseEventListener(_eventManager);
            db.removeContentEventListener(_eventManager);
            db.removeWorkflowEventListener(_eventManager);

            // Notify all managed DB attributes
            Iterator attNames = db.getAttributeNames().iterator();
            while (attNames.hasNext()) {
                Object att = db.getAttribute((String) attNames.next());
                if (att instanceof ManagedDBAttribute) {
                    ((ManagedDBAttribute) att).close();
                }

            }

            // Clear WebTML cache
            try {
                getWebTMLCache().clearForDatabase(key);
            } catch (CacheException e1) {
                log.error("Exception clearing WebTML cache for closing db '" + key + "'", e1);
            }

            // Call event
            fireCoreEvent(new WGACoreEvent(WGACoreEvent.TYPE_CS_DISCONNECTED, db, this));

            // we might have to remove hdb instance
            WGHierarchicalDatabase.removeInstance(db.getDbReference());

            // Close an external personalisation db if there is one 
            if (db.hasAttribute(DBATTRIB_EXTERNAL_SELF_PERSONALISATION_DB)) {
                try {
                    WGDatabase persdb = (WGDatabase) db.getAttribute(DBATTRIB_EXTERNAL_SELF_PERSONALISATION_DB);
                    persdb.close();
                } catch (WGAPIException e) {
                    log.error("Exception closing external personalisation database for db '" + key + "'", e);
                }
            }

            // Close the database itself
            try {
                db.close();
                this.log.info("Closed content database for key \"" + key + "\"");
            } catch (WGAPIException e) {
                log.error("Unable to close content database for key \"" + key + "\"", e);
            }

            // Clear related problems
            _problemRegistry.clearProblemScope(new DatabaseScope(key));

        }

    }

    public void removeCustomShare(String name) {
        ShareDefinition def = getShares().get(name);
        if (def == null) {
            getLog().info("Cannot remove custom share '" + def.getName() + "'. It does not exist.");
        } else if (def.getOrigin() != ShareDefinition.ORIGIN_CUSTOM) {
            getLog().info("Cannot remove custom share '" + def.getName() + "'. It is not custom.");
        }

        getShares().remove(name);
        getLog().info("Removed custom share '" + name + "'");
    }

    public static final String DBATTRIB_LOGGER = "Logger";

    public static final String DBATTRIB_DESIGNSYNC = "designsync";

    /**
     * Sets, if this database should be regarded a db with multilanguage content
     * (true) where the language should get selected by the users preferences,
     * or if all requests for content should retrieve the database's default
     * language (false)
     */
    public static final String DBATTRIB_MULTILANGUAGE_CONTENT = "MultiLanguageContent";

    private static final String DBATTRIB_FIRSTLEVELDBOPTIONS = "FirstLevelDBOptions";

    public static final String DBATTRIB_FIRSTLEVELPUBLISHEROPTIONS = "FirstLevelPublisherOptions";

    public static final String DBATTRIB_FULLY_CONNECTED = "FullyConnected";

    public static final String DBATTRIB_DBGlOBALS = "TMLScriptDBGlobals";

    private static final String CACHENAME_DESIGNFILES = "DesignFiles";

    public static final String DBATTRIB_EXTERNAL_SELF_PERSONALISATION_DB = "ExternalSelfPersonalisationDB";

    public static final String DBATTRIB_FORCE_LABEL_LANGUAGE = "ForceLabelLanguage";

    public static final String DBATTRIB_FALLBACK_LABEL_LANGUAGE = "FallbackLabelLanguage";

    public static final String DBATTRIB_USE_NONFINAL_HT_FEATURES = "UseNonFinalHTFeatures";

    private WGDatabase retrieveContentDB(ContentDatabase config, Map<String, Serializable> dbConnectionFailures) {

        final ProblemOccasion occ = new ConnectDatabaseProblemOccasion(config.getKey());
        _problemRegistry.clearProblemOccasion(occ);

        try {
            final String dbType = (config instanceof ContentStore ? "Web application" : "Data source");

            if (config instanceof ContentStore) {
                logCategoryInfo(dbType + " " + config.getKey(), 2);
            } else {
                logCategoryInfo(dbType + " " + config.getKey(), 2);
            }

            // Get basic information
            String strType = config.getImplClassName();
            String strKey = config.getKey();

            // retrieve dbserver for db
            WGDatabaseServer server = getDatabaseServers().get(config.getDbServer());
            if (server == null) {
                throw Problem.create(occ, "databaseConnectionFailed.unknownServer", ProblemSeverity.HIGH,
                        Problem.var("server", config.getDbServer()));
            }

            if (strKey.equalsIgnoreCase("start") || strKey.equalsIgnoreCase("bi")
                    || strKey.equalsIgnoreCase("static") || strKey.equalsIgnoreCase("statictml")
                    || strKey.startsWith(PluginConfig.PLUGIN_DBKEY_PREFIX)) {
                throw Problem.create(occ, "databaseConnectionFailed.invalidDbkey", ProblemSeverity.HIGH);
            }

            // Look if the server is able and allowed to instantiate this db type
            Class<WGDatabaseCore> typeClass;
            try {
                Class theClass = WGFactory.getImplementationLoader().loadClass(strType);
                if (!WGDatabaseCore.class.isAssignableFrom(theClass)) {
                    throw new WGAServerException(
                            "Cannot map " + dbType + " to key \"" + strKey + "\" - Database implementation class \""
                                    + strType + "\" is no WGDatabaseCore implementation.");
                }
                typeClass = theClass;
            } catch (Exception e) {
                throw Problem.create(occ, "databaseConnectionFailed.invalidImplementation", ProblemSeverity.HIGH,
                        Problem.var("class", strType), e);
            }

            // Test for retrievable design for content stores
            if (config instanceof ContentStore) {
                ContentStore csConfig = (ContentStore) config;
                if (csConfig.getDesign() != null) {
                    try {
                        WGADesign design = getDesignManager().getDesignForConfig(csConfig.getDesign());
                        if (design == null) {
                            throw Problem.create(occ, "applyDesignProblem.unknownDesign", ProblemSeverity.HIGH,
                                    Problem.var("design", (new DesignReference(csConfig.getDesign()).toString())));
                        }
                    } catch (Problem p) {
                        throw p;
                    } catch (WGADesignRetrievalException e) {
                        String design = (new DesignReference(csConfig.getDesign())).toString();
                        if (e.getDesign() != null) {
                            design = e.getDesign();
                        }
                        throw Problem.create(occ, "applyDesignProblem.unretrievableDesign", ProblemSeverity.HIGH,
                                Problem.var("design", design), e);
                    } catch (Exception e) {
                        throw Problem.create(occ, "applyDesignProblem.invalidDesign", ProblemSeverity.HIGH,
                                Problem.var("design", (new DesignReference(csConfig.getDesign()).toString())), e);
                    }
                }
            }

            // Evaluate title
            String title = config.getTitle();

            // Evaluate domain
            /*
            Element domainElement = (Element) databaseElement.selectSingleNode("domain");
            DomainConfiguration domainConfig = new DomainConfiguration();
                
            if (domainElement != null) {
            domainConfig = getDomainConfig(domainElement.getStringValue());
            }*/
            Domain domainCfg = getWgaConfiguration().getDomain(config.getDomain());
            WGADomain domain = getDomain(domainCfg);

            // Collect db options from global, server, database
            HashMap<String, String> dbOptions = new HashMap<String, String>();
            putDefaultDbOptions(dbOptions);
            dbOptions.putAll(_wgaConfiguration.getGlobalDatabaseOptions());
            dbOptions.putAll(config.getDatabaseOptions());

            // We collect those options that were added on database level in here
            // and add this set as database attribute
            // so we can tell them later from lower priority options
            Set<String> firstLevelDBOptions = new HashSet<String>();
            firstLevelDBOptions.addAll(config.getDatabaseOptions().keySet());

            // Mandatory db options
            dbOptions.put(WGDatabase.COPTION_DBREFERENCE, strKey.toLowerCase());

            // Optionally add hotpatches path
            String hotPatchesPath = System.getProperty(SYSPROPERTY_JDBC_HOTPATCHES);
            if (hotPatchesPath != null) {
                File hotPatchesFile = getWGAFile(hotPatchesPath);
                if (hotPatchesFile != null) {
                    dbOptions.put(WGDatabaseImpl.COPTION_HOTPATCH, hotPatchesFile.getAbsolutePath());
                }
            }

            // get the database object
            WGDatabase db = null;

            if (config.isLazyConnecting()) {
                db = server.prepareDatabase(typeClass, dbOptions);
            } else {
                db = server.openDatabase(typeClass, dbOptions);
            }

            if (db == null || (!config.isLazyConnecting() && !db.isSessionOpen())) {
                throw new WGAServerException("Could not open database for key " + strKey.toLowerCase()
                        + " - Check logged messages from the application log for error details");
            }

            try {

                if (!config.isLazyConnecting()) {
                    db.getSessionContext().setTask("Initializing database in WGA");
                    log.info("Mapping " + dbType + " on path \"" + db.getPath() + "\" (" + db.getTypeName()
                            + ") to database key \"" + strKey.toLowerCase() + "\"");
                    try {
                        if (db.getContentStoreVersion() >= WGDatabase.CSVERSION_WGA5) {
                            int patchLevel = db.getContentStorePatchLevel();
                            log.info("Database for " + strKey.toLowerCase() + " is a WGA Content Store of version "
                                    + db.getContentStoreVersion()
                                    + (patchLevel != 0 ? " at patch level " + patchLevel : ""));
                        }
                    } catch (WGAPIException e) {
                        throw new WGAServerException(
                                "Exception determining content store version of database " + strKey.toLowerCase(),
                                e);
                    }
                } else {
                    log.info("Preparing " + dbType + " on path \"" + db.getPath() + "\" (" + db.getTypeName()
                            + ") as database for key \"" + strKey.toLowerCase() + "\"");
                }

                if (title != null) {
                    db.setTitle(title);
                }

                // Inject the default language if configured, else trigger determination
                if (config instanceof ContentStore) {
                    ContentStore csConfig = (ContentStore) config;
                    if (!WGUtils.isEmpty(csConfig.getDefaultLanguage())) {
                        db.setDefaultLanguage(csConfig.getDefaultLanguage());
                    } else {
                        db.onConnect(new DatabaseAction() {
                            @Override
                            public void run(WGDatabase db) throws Exception {
                                try {
                                    db.determineDefaultLanguage();
                                } catch (WGAPIException e) {
                                    getLog().error("Exception determining default language for " + dbType + " "
                                            + db.getDbReference(), e);
                                    _problemRegistry.addProblem(Problem.create(occ,
                                            "databaseDefaultLanguageProblem.invalidDefaultLanguage",
                                            ProblemSeverity.LOW, e));
                                }
                            }
                        });
                    }
                }
                ;

                // Set mandatory database attributes
                initializeDBAttributes(db, strKey, domain.getName(), firstLevelDBOptions);

                // Inject domain authentication module
                if (domain.getAuthModule() != null) {
                    try {
                        db.setAuthenticationModule(new DomainRedirectionAuthModule(this, domain.getName()));
                    } catch (Exception e) {
                        throw Problem.create(occ, "databaseConnectionFailed.invalidAuth", ProblemSeverity.HIGH, e);
                    }
                }

                // Configure design provider, if neccessary
                if (config instanceof ContentStore) {
                    ContentStore csConfig = (ContentStore) config;
                    if (csConfig.getDesign() != null) {
                        getDesignManager().applyDesign(db, csConfig, occ);
                    }
                }

                // Determine if ACL is empty
                // If so, eventually add domain default manager
                boolean aclEmpty = false;
                try {
                    if (db.isConnected() && db.isSessionOpen() && db.hasFeature(WGDatabase.FEATURE_ACL_MANAGEABLE)
                            && db.getACL().getAllEntries().size() == 0) {
                        aclEmpty = true;
                    }
                } catch (WGBackendException e1) {
                    getLog().error("Error retrieving ACL state of " + dbType + " '" + db.getDbReference() + "'",
                            e1);
                }

                if (aclEmpty && domain.getConfig().getDefaultManager() != null) {
                    try {
                        getLog().info("Adding default manager '" + domain.getConfig().getDefaultManager()
                                + "' to ACL of '" + strKey + "'");
                        db.getACL().createUserEntry(domain.getConfig().getDefaultManager(),
                                WGDatabase.ACCESSLEVEL_MANAGER);
                    } catch (WGAPIException e) {
                        getLog().error("Exception on adding default manager to ACL of '" + strKey + "'", e);
                    }
                }

                // Process system container
                SystemContainerManager.SystemContainerContext scContext = null;
                try {
                    scContext = _systemContainerManager.addDatabase(db, aclEmpty);
                } catch (Problem p) {
                    throw p;
                } catch (InvalidCSConfigVersionException e) {
                    throw Problem.create(occ, "databaseConnectionFailed.incompatibleDesign", ProblemSeverity.HIGH,
                            Problem.var("targetversion", e.getTargetVersion()), e);
                } catch (Exception e) {
                    throw Problem.create(occ, "databaseConnectionFailed.invalidDesign", ProblemSeverity.HIGH, e);
                }

                // Merge publisher options from global, server, design, database
                Map<String, String> publisherOptions = new HashMap<String, String>();
                publisherOptions.putAll(_wgaConfiguration.getGlobalPublisherOptions());
                if (scContext != null) {
                    scContext.putPublisherOptions(publisherOptions);
                }
                publisherOptions.putAll(config.getPublisherOptions());

                // We collect those options that were added on database level in here
                // and add this set as database attribute
                // so we can tell them later from lower priority options
                Set<String> firstLevelPublisherOptions = new HashSet<String>();
                firstLevelPublisherOptions.addAll(config.getPublisherOptions().keySet());
                db.setAttribute(DBATTRIB_FIRSTLEVELPUBLISHEROPTIONS, firstLevelPublisherOptions);

                // Publisher options initialisation, which is equal for content dbs and plugins
                processPublisherOptions(db, publisherOptions);

                // check if db is empty before hdb script runs
                boolean isEmptyDB = false;
                try {
                    isEmptyDB = (db.isConnected() && db.hasFeature(WGDatabase.FEATURE_FULLCONTENTFEATURES)
                            && db.isContentEmpty());
                } catch (WGAPIException e) {
                    this.log.error("Unable to check if database '" + db.getDbReference() + "' is empty.", e);
                }

                // Validate default language definition
                if (!isEmptyDB && db.hasFeature(WGDatabase.FEATURE_FULLCONTENTFEATURES)) {
                    db.onConnect(new ValidateDefaultLanguageAction());
                }

                // Eventually prepare "external self-personalisation"
                if (db.hasFeature(WGDatabase.FEATURE_FULLCONTENTFEATURES)
                        && domain.getConfig().getPersonalisation() == null
                        && !db.hasFeature(WGDatabase.FEATURE_SELF_PERSONALIZABLE)) {
                    try {
                        createExternalSelfPersonalisation(db);
                    } catch (WGAPIException e) {
                        this.log.error("Exception creating external personalisation database for db '"
                                + db.getDbReference() + "'. Profiles will not be available.", e);
                        _problemRegistry.addProblem(Problem.create(occ,
                                "databaseConnectionProblem.invalidExternalPersDb", ProblemSeverity.LOW, e));
                    }
                }

                // Add listeners for events and register embedded event scripts
                db.addDatabaseEventListener(_eventManager);
                db.addContentEventListener(_eventManager);
                db.addWorkflowEventListener(_eventManager);
                if (db.isConnected()) {
                    _eventManager.updateDatabaseEvents(db);
                }

                // Add file annotators
                updateFileAnnotators(db);

                // add File Converter
                db.setFileConverter(_fileConverter);

                // Do system container initialisations
                if (scContext != null) {
                    scContext.performInitialisation(new Boolean(isEmptyDB));
                    // Revalidate the default language if the database was empty before initialisation, might have new definitions now
                    if (isEmptyDB) {
                        db.onConnect(new ValidateDefaultLanguageAction());
                    }
                }

                // Mark this database as fully connected
                db.setAttribute(DBATTRIB_FULLY_CONNECTED, "true");

                if (_externalFileServingConfig.isEnabled()
                        && db.getBooleanAttribute(DBATTRIB_EXTERNAL_FILE_SERVING_ENABLED, false)) {
                    getLog().info("External file serving enabled for database '" + db.getDbReference() + "'.");
                }

                return db;
            } catch (Throwable e) {
                // Cleanup if anything after initial connecting went wrong which makes using this db impossible
                if (db.isReady()) {
                    try {
                        db.close();
                    } catch (Exception e2) {
                        // Swallowing this exception as anything went wrong before anyway
                    }
                }
                throw e;
            }

        } catch (Problem problem) {
            _problemRegistry.addProblem(problem);
            dbConnectionFailures.put(config.getKey(), problem.getMessage());
            log.error(problem.getMessage(), problem.getCause());
            return null;

        } catch (Throwable e) {
            Problem problem;
            if (e.getCause() instanceof LicenseException) {
                problem = Problem.create(occ, "databaseConnectionFailed.licenseError", ProblemSeverity.HIGH, e);
            } else {
                problem = Problem.create(occ, "databaseConnectionFailed.exception", ProblemSeverity.HIGH, e);
            }
            _problemRegistry.addProblem(problem);
            dbConnectionFailures.put(config.getKey(), e.getMessage());
            log.error("An unexpected exception occurred connecting database " + config.getKey(), e);
            return null;
        }

    }

    private void putDefaultDbOptions(Map<String, String> dbOptions) {
        dbOptions.put(WGDatabase.COPTION_USERCACHELATENCY,
                String.valueOf(_wgaConfiguration.getUserCacheLatencyMinutes()));
        dbOptions.put(WGDatabase.COPTION_LEGACY_DBCP_MONITORING, String.valueOf("true".equals(_wgaConfiguration
                .getServerOptions().get(WGAConfiguration.SERVEROPTION_SERVICE_INTEGRATEDJMX_LEGACY_DBCP))));
    }

    private void createExternalSelfPersonalisation(WGDatabase db) throws WGAPIException {

        File dbDir = new File(getWgaDataDir(), EXTERNAL_PERSDBS_FOLDER);
        if (!dbDir.exists()) {
            dbDir.mkdir();
        }

        Map<String, String> dbOptions = new HashMap<String, String>();
        dbOptions.put(WGDatabase.COPTION_DBREFERENCE, db.getDbReference() + "$pers");
        dbOptions.put(WGDatabase.COPTION_READERPROFILECREATION, "true");
        dbOptions.put(WGDatabase.COPTION_USERCACHELATENCY,
                String.valueOf(_wgaConfiguration.getUserCacheLatencyMinutes()));
        dbOptions.put(WGDatabase.COPTION_WORKFLOWENGINE, WGDefaultWorkflowEngine.class.getName()); // We do not want any real workflow here

        WGDatabase persdb = WGFactory.getInstance().openDatabase(null,
                de.innovationgate.webgate.api.hsql.WGDatabaseImpl.class.getName(),
                dbDir.getPath() + "/" + db.getDbReference(), "sa", "", dbOptions);
        db.setAttribute(DBATTRIB_EXTERNAL_SELF_PERSONALISATION_DB, persdb);

    }

    private WGADomain getDomain(Domain domain) {
        return getDomains(domain.getName());
    }

    private void initializeDBAttributes(WGDatabase db, String dbKey, String domainName, Set firstLevelDBOptions) {
        db.setAttribute(DBATTRIB_DBKEY, dbKey.toLowerCase());
        db.setAttribute(DBATTRIB_DOMAIN, domainName);
        db.setAttribute(DBATTRIB_FIRSTLEVELDBOPTIONS, firstLevelDBOptions);
        db.setAttribute(DBATTRIB_DBGlOBALS, new TMLScriptAppGlobalRegistry());
        db.setAttribute(DBATTRIB_SCOPEOBJECTREGISTRY, new ScopeObjectRegistry(ObjectScope.APP,
                "Database " + db.getDbReference(), new NoopScopeObjectContextCreator()));
        Iterator defaultAtts = _dbDefaultAttributes.entrySet().iterator();
        while (defaultAtts.hasNext()) {
            Map.Entry entry = (Map.Entry) defaultAtts.next();
            db.setAttribute((String) entry.getKey(), (String) entry.getValue());
        }
    }

    public File getWGAFile(String fileName) {

        // Path variables
        fileName = replaceWGAPathVariables(fileName);

        // Dir links
        File file = WGUtils.resolveDirLink(new File(fileName));

        // First try: Use as absolute path
        if (file.exists()) {
            return file;
        }

        // Second try: Find inside wgaconfig path
        File wgaConfigPath = configFile.getParentFile();
        file = new File(wgaConfigPath, fileName);
        if (file.exists()) {
            return file;
        } else {
            return null;
        }
    }

    public String replaceWGAPathVariables(String fileName) {
        return WGUtils.strReplace(fileName, "${", _replaceProcessorInstance, true);
    }

    public File getWGAFolder(String fileName) {
        return getWGAFile(fileName);
    }

    public File getOrCreateWGAFolder(String fileName) {

        // Path variables
        fileName = replaceWGAPathVariables(fileName);

        // Dir links
        File file = WGUtils.resolveDirLink(new File(fileName));

        // First try: Use as absolute path
        if (file.exists()) {
            return file;
        } else if (file.isAbsolute()) {
            file.mkdir();
            if (file.exists() && file.isDirectory()) {
                return file;
            } else {
                return null;
            }
        }

        // Second try: Find inside wgaconfig path
        File wgaConfigPath = configFile.getParentFile();
        file = new File(wgaConfigPath, fileName);
        if (file.exists()) {
            return file;
        } else {
            file.mkdir();
            if (file.exists() && file.isDirectory()) {
                return file;
            } else {
                return null;
            }
        }
    }

    public WGDatabase retrievePersonalisationDB(Domain domainConfig) throws WGAServerException {
        PersonalisationDatabase config = domainConfig.getPersonalisation();

        // Get basic information
        String strType = config.getImplClassName();

        // Look if impl class is reachable
        Class<WGDatabaseCore> typeClass;
        try {
            Class theClass = Class.forName(strType, true, WGFactory.getImplementationLoader());
            if (!WGDatabaseCore.class.isAssignableFrom(theClass)) {
                throw new WGAServerException("Cannot connect personalisation database for domain \""
                        + domainConfig.toString() + "\" - Database implementation class \"" + strType
                        + "\" is no WGDatabaseCore implementation.");
            }
            typeClass = theClass;
        } catch (ClassNotFoundException e) {
            throw new WGAServerException(
                    "Cannot attach personalisation database to domain \"" + domainConfig.toString()
                            + "\" - Database implementation class \"" + strType + "\" not available.");
        } catch (NoClassDefFoundError e) {
            throw new WGAServerException("Cannot attach personalisation database to domain \""
                    + domainConfig.toString() + "\" - Database implementation class or dependent class for type \""
                    + strType + "\" not found: \"" + e.getMessage());
        } catch (VerifyError e) {
            log.error("Cannot attach personalisation database to domain \"" + domainConfig.toString()
                    + "\" - Verify error: " + e.getMessage());
            return null;
        }

        // Retrieve server
        WGDatabaseServer server = getDatabaseServers().get(config.getDbServer());
        if (server == null) {
            throw Problem.create(new UpdateConfigOccasion(), new DomainScope(domainConfig.getName()),
                    "updateConfigProblem.domainPersServerUnknown", ProblemSeverity.HIGH,
                    Problem.var("serverid", config.getDbServer()));
        }

        // Merge db options from global, server, database
        HashMap<String, String> dbOptions = new HashMap<String, String>();
        putDefaultDbOptions(dbOptions);
        dbOptions.putAll(_wgaConfiguration.getGlobalDatabaseOptions());
        dbOptions.putAll(server.getOptions());
        dbOptions.putAll(config.getDatabaseOptions());

        // Mandatory db options
        dbOptions.put(WGDatabase.COPTION_DBREFERENCE, "personalisation_" + domainConfig.getUid());

        // get the database object
        WGDatabase db = null;
        try {
            if (config.isLazyConnecting()) {
                db = server.prepareDatabase(typeClass, dbOptions);
            } else {
                db = server.openDatabase(typeClass, dbOptions);
            }
        } catch (WGAPIException e) {
            throw new WGAServerException("Could not connect to database", e);
        } catch (ModuleDependencyException e) {
            throw new WGAServerException(
                    "Could not connect to database because of missing dependency: " + e.getMessage());
        }

        if (db == null) {
            throw new WGAServerException("Could not open personalisation database for domain '"
                    + domainConfig.getName() + " - Check logged messages above for error details");
        } else if (!db.getRoles().contains(WGDatabase.ROLE_USERPROFILES)) {
            throw new WGAServerException("Could not open personalisation database \for domain '"
                    + domainConfig.getName() + " - This type does not deliver user profiles");
        }

        if (config.isLazyConnecting()) {
            log.info("Preparing personalisation database on path \"" + db.getPath() + "\" for domain \""
                    + domainConfig.getName() + "\"");
            db.addDatabaseConnectListener(this);
        } else {
            log.info("Attaching personalisation database on path \"" + db.getPath() + "\" to domain \""
                    + domainConfig.getName() + "\"");
            try {
                log.info("Personalisation database of domain " + domainConfig.getName()
                        + " is content store version " + db.getContentStoreVersion());
            } catch (WGAPIException e) {
                throw new WGAServerException(
                        "Exception determining content store version of personalisation database for domain "
                                + domainConfig.getName(),
                        e);
            }
        }

        // Set domain
        db.setAttribute(WGACore.DBATTRIB_DOMAIN, domainConfig.getUid());

        // Mark this database as fully connected
        db.setAttribute(DBATTRIB_FULLY_CONNECTED, "true");

        return db;
    }

    private ServletContext _context;

    protected int tmlBuffer = 8;

    protected String tmlHeader = "";

    private Map<String, ShareDefinition> _shares = new HashMap<String, ShareDefinition>();

    public synchronized void updateContentDBs(boolean obsoleteBogusParam) {
        updateContentDBs();
    }

    /**
     * 
     * @return set containing dbkeys which where initial connected in this
     *         configUpdate
     */
    public synchronized void updateContentDBs() {

        logCategoryInfo("Web applications and data sources", 1);

        // contains dbkeys that where initial connected in this configUpdate
        HashSet<String> newConnectedDBKeys = new HashSet<String>();

        WGFactory.getInstance().closeSessions();

        //Element databaseRoot = (Element) this.configDocument.getRootElement().selectSingleNode("contentdbs");
        //List databaseElements = databaseRoot.selectNodes("contentdb");
        //Element databaseElement;
        ContentDatabase databaseConfig;
        WGDatabase db;
        Set<String> currentDBs = new HashSet<String>();

        // db connection errors mapped by dbkey
        Map<String, Serializable> dbConnectionFailures = new HashMap<String, Serializable>();

        List<ContentDatabase> databases = _wgaConfiguration.getContentDatabases();

        // Map new databases and update current databases
        for (int idxDB = 0; idxDB < databases.size(); idxDB++) {

            databaseConfig = databases.get(idxDB);
            String dbKey = databaseConfig.getKey();

            // ignore disabled dbs
            ConnectDatabaseProblemOccasion occ = new ConnectDatabaseProblemOccasion(databaseConfig.getKey());
            _problemRegistry.clearProblemOccasion(occ);
            if (!databaseConfig.isEnabled()) {
                continue;
            }
            DatabaseServer serverConfig = (DatabaseServer) _wgaConfiguration.getByUid(databaseConfig.getDbServer());
            if (serverConfig != null && !serverConfig.isEnabled()) {
                continue;
            }

            // Get or create database object
            boolean isNewDB = false;
            if (this.contentdbs.containsKey(dbKey) == false) {
                try {
                    db = retrieveContentDB(databaseConfig, dbConnectionFailures);
                    if (db == null) {
                        continue;
                    }

                    this.contentdbs.put(dbKey, db);
                    String uuid = db.getUUID();
                    if (uuid != null) {
                        this._contentdbUuidsToKeys.put(uuid, dbKey);
                    }

                    isNewDB = true;
                    newConnectedDBKeys.add(dbKey);
                } catch (Throwable e) {
                    getLog().error("Exception connecting database " + dbKey, e);
                    if (this.contentdbs.containsKey(dbKey)) {
                        this.contentdbs.remove(dbKey);
                    }
                    _problemRegistry.addProblem(
                            Problem.create(occ, "databaseConnectionFailed.exception", ProblemSeverity.HIGH, e));
                    continue;
                }
            } else {
                db = this.contentdbs.get(dbKey);
            }
            currentDBs.add(dbKey);

            updateFieldMappings(db, databaseConfig.getFieldMappings());

            // Update client restrictions
            if (databaseConfig.isClientRestrictionsEnabled()) {

                List<IPRestriction> restrictions = new ArrayList<>();
                boolean errornousRestrictions = false;
                for (ClientRestriction clientRes : databaseConfig.getClientRestrictions()) {
                    try {
                        IPRestriction ipRes = IPs.parseRestriction(clientRes);
                        restrictions.add(ipRes);
                    } catch (Exception e) {
                        getLog().error("Exception parsing client restriction on database " + dbKey + ": "
                                + clientRes.toString(), e);
                        _problemRegistry.addProblem(
                                Problem.create(occ, "databaseConnectionProblem.invalidClientRestriction",
                                        ProblemSeverity.HIGH, Problem.var("restriction", clientRes.toString()), e));
                        errornousRestrictions = true;
                    }
                }

                db.setAttribute(DBATTRIB_CLIENTRESTRICTIONS, restrictions);

            } else {
                // if not enabled remove clientRestrictions from db
                db.removeAttribute(DBATTRIB_CLIENTRESTRICTIONS);
            }

            // Operations only for newly connected databases
            if (isNewDB) {
                performNewDBOperations(db);
            }
        }

        // Unmap removed databases
        Set<String> removedDBs = new HashSet<String>(this.contentdbs.keySet());
        removedDBs.removeAll(currentDBs);
        Iterator<String> removedIt = removedDBs.iterator();
        while (removedIt.hasNext()) {
            String key = removedIt.next();
            if (!key.startsWith(PluginConfig.PLUGIN_DBKEY_PREFIX)) {
                removeContentDB(key);
            }
        }

        // notify LuceneManager
        if (luceneManager != null) {
            luceneManager.configurationHasChanged(newConnectedDBKeys);
        }

        // inform admin of connection errors
        if (!dbConnectionFailures.isEmpty()) {
            WGAMailNotification notification = new WGAMailNotification(
                    WGAMailNotification.TYPE_DATABASE_INITIALISATION_ERRORS);
            notification.setSubject("Some databases could not be initialized successfully");
            notification.append("<b>The following databases have reported errors during initialisation:</b>");
            Iterator<String> dbkeys = dbConnectionFailures.keySet().iterator();
            while (dbkeys.hasNext()) {
                String dbkey = dbkeys.next();
                notification.append("<br><br><b>Database:</b> " + dbkey + "<br>");
                Object failure = dbConnectionFailures.get(dbkey);
                if (failure instanceof String) {
                    notification.append((String) failure);
                } else if (failure instanceof Throwable) {
                    Throwable throwable = (Throwable) failure;
                    if (throwable != null) {
                        notification.append(throwable);
                    }

                }
            }
            notification.setAttachLogfile(true);
            send(notification);
        }
    }

    private void updateShares() {

        logCategoryInfo("Shares", 1);

        // Add all shares from configuration
        Iterator<Share> shareConfigs = getWgaConfiguration().getShares().iterator();
        Map<String, ShareDefinition> newShares = new HashMap<String, ShareDefinition>();
        while (shareConfigs.hasNext()) {
            Share shareConfig = shareConfigs.next();
            if (!shareConfig.isEnabled()) {
                continue;
            }

            ModuleDefinition shareModDefinition = getModuleRegistry().getModuleDefinition(ShareModuleType.class,
                    shareConfig.getImplClassName());
            if (shareModDefinition == null) {
                getLog().error("Unknown content share type '" + shareConfig.getImplClassName() + "'");
                getProblemRegistry().addProblem(
                        Problem.create(new UpdateConfigOccasion(), new ContentShareScope(shareConfig.getName()),
                                "updateConfigProblem.shareTypeUnknown", ProblemSeverity.LOW));
                continue;
            }

            ShareProperties props = (ShareProperties) shareModDefinition.getProperties();
            try {
                ShareDefinition shareDefinition = props.createShareDefinition(shareConfig);
                shareDefinition.setOrigin(ShareDefinition.ORIGIN_WGACONFIG);
                getLog().info("Initializing content share '" + shareConfig.getName() + "'");
                shareDefinition.init(this);
                newShares.put(shareConfig.getName(), shareDefinition);
            } catch (ShareInitException e) {
                getLog().error("Unable to initialize content share '" + shareConfig.getName()
                        + "' because of the following errors:");
                getProblemRegistry().addProblem(
                        Problem.create(new UpdateConfigOccasion(), new ContentShareScope(shareConfig.getName()),
                                "updateConfigProblem.shareInitException", ProblemSeverity.LOW, e));
                Iterator msgs = e.getDetailMessages().iterator();
                while (msgs.hasNext()) {
                    getLog().error("- " + msgs.next());
                }
            } catch (Exception e) {
                getLog().error("Exception initializing content share '" + shareConfig.getName() + "'", e);
                getProblemRegistry().addProblem(
                        Problem.create(new UpdateConfigOccasion(), new ContentShareScope(shareConfig.getName()),
                                "updateConfigProblem.shareInitException", ProblemSeverity.LOW, e));
            }
        }

        // Add custom shares that were added to the previous share configs
        if (_shares != null) {
            Iterator<Map.Entry<String, ShareDefinition>> previousConfigs = _shares.entrySet().iterator();
            while (previousConfigs.hasNext()) {
                Map.Entry<String, ShareDefinition> shareEntry = previousConfigs.next();
                ShareDefinition config = (ShareDefinition) shareEntry.getValue();
                if (config.getOrigin() == ShareDefinition.ORIGIN_CUSTOM) {
                    newShares.put(shareEntry.getKey(), config);
                }
            }
        }

        // Set new shares map
        _shares = newShares;

    }

    private void performNewDBOperations(WGDatabase db) {

        // When lazily connected: register for database connection event
        // to load events and show a message when it happens
        if (!db.isConnected()) {
            db.addDatabaseConnectListener(this);
        }

        // Call content store connection event
        fireCoreEvent(new WGACoreEvent(WGACoreEvent.TYPE_CS_CONNECTED, db, this));
    }

    protected void updateFieldMappings(WGDatabase db, List<FieldMapping> fieldMappings) {

        Map<String, String> metaMappings = null;
        Map<String, String> itemMappings = null;
        Map pluginShortcuts = null;

        // Get the mapping maps from containers
        boolean dbMappingRoot = false;
        metaMappings = (Map<String, String>) db.getAttribute(DBATTRIB_META_MAPPINGS);
        itemMappings = (Map<String, String>) db.getAttribute(DBATTRIB_ITEM_MAPPINGS);
        pluginShortcuts = (Map) db.getAttribute(DBATTRIB_PLUGIN_SHORTCUTS);
        dbMappingRoot = true;

        // Mappings that only apply to whole dbs
        if (dbMappingRoot == true) {
            buildDesignShortcuts(db);
        }

        if (fieldMappings != null) {
            Iterator<FieldMapping> mappings = fieldMappings.iterator();
            while (mappings.hasNext()) {
                FieldMapping mapping = mappings.next();
                if (mapping.getType().equals(FieldMapping.TYPE_META)) {
                    metaMappings.put(mapping.getName(), mapping.getExpression());
                } else if (mapping.getType().equals(FieldMapping.TYPE_ITEM)) {
                    itemMappings.put(mapping.getName(), mapping.getExpression());
                }
            }
        }
    }

    protected void buildDesignShortcuts(WGDatabase db) {

        Map metaMappings = (Map) db.getAttribute(DBATTRIB_META_MAPPINGS);
        Map itemMappings = (Map) db.getAttribute(DBATTRIB_ITEM_MAPPINGS);
        Map pluginShortcuts = (Map) db.getAttribute(DBATTRIB_PLUGIN_SHORTCUTS);

        CSConfig csconfig = (CSConfig) db.getAttribute(DBATTRIB_CSCONFIG);
        if (csconfig != null && csconfig instanceof de.innovationgate.wga.common.beans.csconfig.v2.CSConfig) {
            de.innovationgate.wga.common.beans.csconfig.v2.CSConfig v2 = (de.innovationgate.wga.common.beans.csconfig.v2.CSConfig) csconfig;
            Iterator shortcuts = v2.getShortcuts().iterator();
            while (shortcuts.hasNext()) {
                Shortcut shortcut = (Shortcut) shortcuts.next();
                if (shortcut.getType() == Shortcut.TYPE_ITEM_MAPPING
                        || shortcut.getType() == Shortcut.TYPE_META_MAPPING
                        || shortcut.getType() == Shortcut.TYPE_PLUGIN) {
                    Map<String, String> mappings = (Map<String, String>) (shortcut
                            .getType() == Shortcut.TYPE_ITEM_MAPPING ? itemMappings
                                    : shortcut.getType() == Shortcut.TYPE_META_MAPPING ? metaMappings
                                            : pluginShortcuts);
                    mappings.put(shortcut.getShortcut(), shortcut.getReference());
                }
            }
        }
    }

    private String defaultMediaKey = null;

    private static String _buildSignature;

    private Cache _calledSequenceIds = null;

    private static Map<String, String> _dbDefaultAttributes = new HashMap<String, String>();
    private static Set<String> _licenseFreeDatabases = new HashSet<String>();
    static {

        // Init the default db attributes for content stores
        _dbDefaultAttributes.put(WGACore.DBATTRIB_QUERY_DEFAULT, "native");
        _dbDefaultAttributes.put(WGACore.DBATTRIB_EXPRESSION_DEFAULT, "tmlscript");
        _dbDefaultAttributes.put(WGACore.DBATTRIB_DEFAULT_MEDIAKEY, ENCODER_HTML);
        _dbDefaultAttributes.put(WGACore.DBATTRIB_DEFAULT_ITEM_ENCODING, "none");
        _dbDefaultAttributes.put(WGACore.DBATTRIB_MULTILANGUAGE_CONTENT, "true");
        _dbDefaultAttributes.put(WGACore.DBATTRIB_HOME_PAGE, "");
        _dbDefaultAttributes.put(WGACore.DBATTRIB_LOGIN_PAGE, "");

        // Init the license-free database keys
        _licenseFreeDatabases.add("plugin-management");

    }

    private File wgaTempDir = null;

    private LuceneManager luceneManager = null;

    private Map<String, Analyzer> analyzerMappings = Collections.synchronizedMap(new HashMap<String, Analyzer>());

    private static final Set<String> LUCENE_STOP_WORDS = new HashSet<String>();
    static {
        LUCENE_STOP_WORDS.add("t");
        LUCENE_STOP_WORDS.add("s");
    }
    private Analyzer defaultAnalyzer = new StandardAnalyzer(org.apache.lucene.util.Version.LUCENE_35,
            LUCENE_STOP_WORDS);

    // lucene filehandler mapped by file-extension e.g. 'pdf', 'txt', 'xml'
    private Map<String, FileHandler> fileHandlerMappings = Collections
            .synchronizedMap(new HashMap<String, FileHandler>());

    // filter mappings for custom javax.servlet.filters
    private List<WGAFilterConfig> filterMappings = new LinkedList<WGAFilterConfig>();

    private UserAgentVerifier userAgentVerifier;

    private DESEncrypter desEncrypter;

    private List<WGACoreEventListener> coreEventListeners = Collections
            .synchronizedList(new LinkedList<WGACoreEventListener>());
    private List<WGAConfigurationUpdateListener> configUpdateListeners = Collections
            .synchronizedList(new LinkedList<WGAConfigurationUpdateListener>());

    // TestCore for test facility (assertions via tml:script)
    private TestCore _testCore;

    private DesignFileValidator designFileValidator;

    private boolean _webserviceEnabled;

    /**
     * character encoding used by this wga instance used for request encoding,
     * file upload encoding, encoding of deployed tmls, etc.
     */
    private String _characterEncoding = null;

    private WGAUsageStatistics _usageStatistics;

    private Set customCoreListenerClassnames;

    private static IsolatedJARLoader baseLibraryLoader;

    private Map<String, HttpSession> _activeHttpSessions = new ConcurrentHashMap<String, HttpSession>();

    public Map<String, HttpSession> getActiveHttpSessions() {
        return _activeHttpSessions;
    }

    private LogServer _logServer;

    private WGAMailConfiguration _mailConfig;

    private Cache designFileCache;

    private WebTMLCache _webTMLCache = null;

    private boolean _webTMLCachingEnabled = true;

    public boolean isWebTMLCachingEnabled() {
        return _webTMLCachingEnabled;
    }

    public void setWebTMLCachingEnabled(boolean enabled) {
        if (enabled) {
            getLog().info("Enabling WebTML caching");
        } else {
            getLog().info("Disabling WebTML caching");
        }
        _webTMLCachingEnabled = enabled;
    }

    private ClusterService _clusterService;

    private FileDerivateManager fileDerivateManager;

    private XStream _libraryXStream;

    public FileDerivateManager getFileDerivateManager() {
        return fileDerivateManager;
    }

    @SuppressWarnings({ "deprecation", "deprecation" })
    private void initReadGeneralConfig(boolean update) throws CacheException, OptionConversionException {

        logCategoryInfo("General Configuration", 1);

        OptionReader cacheOptions = OptionReader.create(_wgaConfiguration.getServerOptions(),
                new CacheModuleDefinition());

        // WebTML Cache
        int tmlCache = _wgaConfiguration.getWebtmlCacheSize();
        if (_webTMLCache == null) {
            _webTMLCache = new WebTMLCache(this, _wgaConfiguration);
        } else {
            if (_webTMLCache.configure(_wgaConfiguration)) {
                getLog().info(
                        "WebTML Cache configuration updated. Will not be effective until next WebTML cache reset.");
            }
        }

        _webserviceEnabled = _wgaConfiguration.isWebservicesEnabled();

        _characterEncoding = _wgaConfiguration.getCharacterEncoding();
        if (_characterEncoding == null) {
            _characterEncoding = "UTF-8";
        }
        log.info("Using default output encoding '" + _characterEncoding + "'.");

        tmlBuffer = _wgaConfiguration.getTmlBuffer();
        if (_wgaConfiguration.getTmlHeader() != null) {
            tmlHeader = _wgaConfiguration.getTmlHeader();
        } else {
            tmlHeader = "";
        }

        // determine if character encoding changed
        // this is used for SC_NOT_MODIFIED in WGPDispatcher
        if (_characterEncoding != null && getLastCharacterEncoding() == null
                || !getLastCharacterEncoding().equals(_characterEncoding)) {
            // character encoding was not yet set or has changed           
            setLastCharacterEncoding(_characterEncoding);
        }

        // application log
        this.log.setLevel(org.apache.log4j.Level.toLevel(_wgaConfiguration.getApplicationLogLevel()));
        if (_wgaConfiguration.getApplicationLogDirectory() != null) {
            File loggingDir = new File(_wgaConfiguration.getApplicationLogDirectory());
            if (loggingDir.exists()) {
                try {
                    Appender fileAppender = new DailyRollingFileAppender(new PatternLayout("%d{HH:mm:ss} %p %m\n"),
                            loggingDir.getAbsolutePath() + "/" + "wga.log", "yyyy-MM-dd");
                    this.log.addAppender(fileAppender);
                } catch (IOException e) {
                    this.log.error("Error creating application log file", e);
                    _problemRegistry.addProblem(Problem.create(new UpdateConfigOccasion(),
                            "updateConfigProblem.invalidApplicationLog", ProblemSeverity.HIGH, e));
                }
            } else {
                this.log.error("Logging directory " + loggingDir.getAbsolutePath() + " does not exist");
                _problemRegistry
                        .addProblem(Problem.create(new UpdateConfigOccasion(), "updateConfigProblem.missingLogDir",
                                ProblemSeverity.HIGH, Problem.var("dir", loggingDir.getAbsolutePath())));
            }
        }

        // Lucene
        if (update == false && _wgaConfiguration.getLuceneManagerConfiguration().isEnabled()) {
            try {
                this.luceneManager = LuceneManager.retrieve(this,
                        _wgaConfiguration.getLuceneManagerConfiguration());
                log.debug("Lucene option 'maxBooleanClauseCount' set to '"
                        + this.luceneManager.getBooleanQueryMaxClauseCount() + "'.");
                log.debug("Lucene option 'maxDocsPerDBSession' set to '"
                        + this.luceneManager.getMaxDocsPerDBSession() + "'.");
                log.info("Lucene fulltext index enabled using index directory "
                        + this.luceneManager.getIndexDirectory().getAbsolutePath());
            } catch (Exception e) {
                log.error("Unable to initialize Lucene fulltext index: " + e.getClass().getName() + " - ", e);
                _problemRegistry.addProblem(Problem.create(new UpdateConfigOccasion(),
                        "updateConfigProblem.invalidLuceneIndex", ProblemSeverity.HIGH, e));
            }
        }

        // File derivates
        if (update == false) {
            this.fileDerivateManager = new FileDerivateManager(this); // Will be inited later on startup
        } else {
            this.fileDerivateManager.init(_wgaConfiguration);
        }

        // Personalisation
        userAgentVerifier = new UserAgentVerifier(_wgaConfiguration.getPersonalisationConfiguration(), this);

        // Design
        DesignConfiguration designConfig = _wgaConfiguration.getDesignConfiguration();
        designFileValidator = new DesignFileValidator(designConfig, this);
        if (designConfig.getDefaultEncoding() == null) {
            designConfig.setDefaultEncoding(DEFAULT_FILE_ENCODING);
            getLog().info("No default design file encoding configured. Using '" + DEFAULT_FILE_ENCODING + "'.");
        }

        if (designFileCache == null) {
            String designFileCacheSizeStr = _wgaConfiguration.getServerOptions()
                    .get(WGAConfiguration.SERVEROPTION_CACHE_DESIGN_SIZE);
            if (designFileCacheSizeStr == null) {
                designFileCacheSizeStr = String.valueOf(designConfig.getDesignFileCacheSize());
            }
            designFileCache = CacheFactory.createCache(CACHENAME_DESIGNFILES,
                    Integer.parseInt(designFileCacheSizeStr), null);
        }

        // Custom core listeners - Get names here, instantiate later when
        // library loader is inited       
        if (update == false) {
            customCoreListenerClassnames = new HashSet(_wgaConfiguration.getCoreEventListeners());
        }

        // init mail configuration
        _mailConfig = WGAMailConfiguration.create(_wgaConfiguration.getMailConfiguration());
        WGFactory.setHttpClientFactory(new DefaultHttpClientFactory(_wgaConfiguration));

    }

    public ClusterService getClusterService() {
        return _clusterService;
    }

    private void setLastCharacterEncoding(String characterEncoding) {
        Preferences prefs = Preferences.userNodeForPackage(this.getClass());
        prefs.put("LastCharacterEncoding", characterEncoding);
        prefs.putLong("CharacterEncodingModified", System.currentTimeMillis());
    }

    private String getLastCharacterEncoding() {
        Preferences prefs = Preferences.userNodeForPackage(this.getClass());
        return prefs.get("LastCharacterEncoding", null);
    }

    public long getCharacterEncodingLastModified() {
        Preferences prefs = Preferences.userNodeForPackage(this.getClass());
        return prefs.getLong("CharacterEncodingModified", System.currentTimeMillis());
    }

    private void setLastDesignEncoding(String dbkey, String encoding) {
        try {
            Preferences prefs = Preferences.userNodeForPackage(this.getClass());
            prefs.put(createPrefKeyDesignEncoding(dbkey), encoding);
            prefs.putLong(createPrefKeyDesignEncodingLastModified(dbkey), System.currentTimeMillis());
        } catch (Exception e) {
            log.error("Unable to set lastDesignEncoding preferences.", e);
        }
    }

    private String createPrefKeyDesignEncoding(String dbkey) throws NoSuchAlgorithmException {
        return "LastDesignEncoding_" + WGUtils.hashPassword(dbkey.toLowerCase());
    }

    private String createPrefKeyDesignEncodingLastModified(String dbkey) throws NoSuchAlgorithmException {
        return "DesignEncodingModified_" + WGUtils.hashPassword(dbkey.toLowerCase());
    }

    private String getLastDesignEncoding(String dbkey) {
        Preferences prefs = Preferences.userNodeForPackage(this.getClass());
        try {
            return prefs.get(createPrefKeyDesignEncoding(dbkey), null);
        } catch (Exception e) {
            log.error("Unable to retrieve preferences for 'LastDesignEncoding'", e);
            return null;
        }
    }

    public long getDesignEncodingLastModified(String dbkey) {
        Preferences prefs = Preferences.userNodeForPackage(this.getClass());
        try {
            return prefs.getLong(createPrefKeyDesignEncodingLastModified(dbkey), System.currentTimeMillis());
        } catch (Exception e) {
            log.error("Unable to retrieve preferences for 'DesignEncodingModified'", e);
            return System.currentTimeMillis();
        }
    }

    private void initReadMappings() {

        logCategoryInfo("Mappings", 1);

        //Element mappingsRoot = (Element) this.configDocument.getRootElement().selectSingleNode("mappings");

        // get mapping class loader
        buildLibraryLoaders();

        // Default media mappings
        this.systemMediaKeys.put(ENCODER_HTML, new MediaKey(ENCODER_HTML, "text/html", false, false));
        this.systemMediaKeys.put(ENCODER_XML, new MediaKey(ENCODER_XML, "text/xml", false, true));
        this.systemMediaKeys.put("wml", new MediaKey("wml", "text/wml", false, false));
        this.systemMediaKeys.put("pdf", new MediaKey("pdf", "application/pdf", true, true));

        defaultAnalyzer = new StandardAnalyzer(org.apache.lucene.util.Version.LUCENE_35, LUCENE_STOP_WORDS);

        log.info("Registering defaultAnalyzer to '" + defaultAnalyzer.getClass().getName() + "'");

    }

    private synchronized void initReadFilterMappings() {

        logCategoryInfo("Filter mappings", 1);

        List<WGAFilterConfig> newFilterMappings = new LinkedList<WGAFilterConfig>();

        // read filter mappings from registry
        Iterator filters = getModuleRegistry().getModulesForType(FilterConfigModuleType.class).values().iterator();
        while (filters.hasNext()) {
            ModuleDefinition modDef = (ModuleDefinition) filters.next();
            try {
                modDef.testDependencies();
                FilterMapping mapping = (FilterMapping) modDef.getProperties();
                WGAFilterConfig config = WGAFilterConfig.createFromMapping(mapping);
                if (config != null) {
                    log.info("Adding filter '" + config.getFilterName() + "'");
                    newFilterMappings.add(config);
                }
            } catch (ModuleDependencyException e) {
                // Fail silently here. Filters may not yet be ready at this time (#00001870). We notify about filters with missing dependencies later.
            }
        }

        // read filter mappings from config
        Iterator<FilterMapping> mappings = _wgaConfiguration.getFilterMappings().iterator();
        while (mappings.hasNext()) {
            WGAFilterConfig config = WGAFilterConfig.createFromMapping(mappings.next());
            if (config != null) {
                log.info("Adding filter '" + config.getFilterName() + "'");
                newFilterMappings.add(config);
            }
        }
        this.filterMappings = newFilterMappings;
    }

    public void addAnalyzerMapping(String language, String analyzerClassName)
            throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        log.info(
                "Registering analyzer mapping for language code '" + language + "' to '" + analyzerClassName + "'");
        Class analyzerClass = getLibraryLoader().loadClass(analyzerClassName);
        Analyzer analyzer = (Analyzer) analyzerClass.newInstance();
        analyzerMappings.put(language.toLowerCase(), analyzer);
    }

    public void addAnalyzerMapping(String language, Analyzer analyzer) {
        log.info("Registering analyzer mapping for language code '" + language + "' to '"
                + analyzer.getClass().getName() + "'");
        analyzerMappings.put(language.toLowerCase(), analyzer);
    }

    public void removeAnalyzerMapping(String language) {
        log.info("Unregistering analyzer mapping for language code '" + language);
        analyzerMappings.remove(language.toLowerCase());
    }

    public void removeAllAnalyzerMappings() {
        log.info("Unregistering all language specific analyzer mappings");
        analyzerMappings.clear();
    }

    public void addFileHandlerMapping(String extension, String handlerClassName)
            throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        log.info("Registering filehandler for extension '" + extension + "' - '" + handlerClassName + "'.");
        Class fileHandlerClass = getLibraryLoader().loadClass(handlerClassName);
        FileHandler handler = (FileHandler) fileHandlerClass.newInstance();
        fileHandlerMappings.put(extension.toLowerCase(), handler);
    }

    private boolean addElementMapping(String name, String theClass, boolean system) {

        if (system) {
            this.systemElements.put(name, theClass);
            return true;
        } else if (systemElements.containsKey(name)) {
            getLog().warn("Cannot add WebTML element '" + name
                    + "' because the name is already used in WGA configuration");
            return false;
        } else {
            this.customElements.put(name, theClass);
            return true;
        }

    }

    private boolean addEncoderMapping(String name, String theClass, boolean system) {
        try {
            name = name.toLowerCase();
            Class formatterClass = getLibraryLoader().loadClass(theClass);
            Object instance = formatterClass.newInstance();
            if (instance instanceof ObjectFormatter) {
                if (system) {
                    _systemFormatters.put(name, formatterClass);
                } else {
                    if (_systemFormatters.containsKey(name)) {
                        log.warn("Cannot add encoding formatter '" + name
                                + "' because the name is already used by a formatter in WGA configuration");
                        return false;
                    }
                    _customFormatters.put(name, formatterClass);
                }
                return true;
            } else {
                log.error("Encoding formatter '" + theClass + "' does not implement interface "
                        + ObjectFormatter.class.getName());
            }
        } catch (InstantiationException e) {
            log.error("Error instantiating encoding formatter '" + theClass + "':" + e.getMessage());
        } catch (IllegalAccessException e) {
            log.error("Access error instantiating encoding formatter '" + theClass + "':" + e.getMessage());
        } catch (ClassNotFoundException e) {
            log.error("Encoding formatter class '" + theClass + "' could not be found");
        } catch (NoClassDefFoundError e) {
            log.error("Encoding formatter class '" + theClass + "' could not be loaded", e);
        }

        return false;
    }

    private void buildLibraryLoaders() {

        // Build jars list
        List<URL> jarsList = new ArrayList<URL>();

        String libraries = _wgaConfiguration.getServerOptions().get(WGAConfiguration.SERVEROPTION_LIBRARIES);
        if (libraries != null && !libraries.trim().equals("")) {
            StringTokenizer tokens = new StringTokenizer(libraries, ";");
            String token;
            while (tokens.hasMoreTokens()) {

                token = tokens.nextToken();

                if (token.endsWith("/*")) {
                    File libDir = getWGAFile(token.substring(0, token.length() - 2));
                    if (libDir != null && libDir.exists() && libDir.isDirectory()) {
                        for (File libFile : libDir.listFiles()) {
                            if (libFile.getName().endsWith(".jar")) {
                                addLibraryFile(jarsList, libFile);
                            }
                        }
                    } else {
                        log.warn("Library entry is not a valid directory: " + token);
                    }
                } else {
                    File libFile = getWGAFile(token);
                    addLibraryFile(jarsList, libFile);
                }
            }
        }

        // add all jars from <configdir>/libs
        File baseLibDir = getBaseLibraryDir();
        if (baseLibDir != null && baseLibDir.exists() && baseLibDir.canRead()) {
            File[] additionalJarFiles = baseLibDir.listFiles(new FilenameFilter() {

                public boolean accept(File dir, String name) {
                    return name.toLowerCase().endsWith(".jar");
                }

            });

            for (File jarFile : additionalJarFiles) {
                try {
                    jarsList.add(jarFile.toURL());
                    log.info("Registering library '" + jarFile.getAbsolutePath() + "'.");
                } catch (MalformedURLException e) {
                    this.log.warn("Registering library '" + jarFile.getAbsolutePath() + "' failed.", e);
                }
            }
        }

        // Build URL array from jars list
        URL[] loaderURLs = new URL[jarsList.size()];
        for (int idx = 0; idx < jarsList.size(); idx++) {
            loaderURLs[idx] = jarsList.get(idx);
        }

        baseLibraryLoader = new IsolatedJARLoader(loaderURLs, getClass().getClassLoader());
        getLog().info("Creating WGA java library loader");
        libraryClassLoadingChain = new DynamicClassLoadingChain(baseLibraryLoader);
        WGFactory.setImplementationLoader(libraryClassLoadingChain);
        updateLibraryLoader();
        _libraryXStream = new XStream(new Dom4JDriver());
        _libraryXStream.setClassLoader(libraryClassLoadingChain);

    }

    private void addLibraryFile(List<URL> jarsList, File libFile) {
        try {
            if (libFile.exists()) {
                jarsList.add(new URL(libFile.toURI().toString()));
                log.info("Registering class library URL " + libFile.toURI().toString());
            } else {
                log.warn("Library entry is not a valid file: " + libFile.getAbsolutePath());
            }

        } catch (MalformedURLException e) {
            log.warn("Exception adding class Library URL for: " + libFile.getAbsolutePath(), e);
        }
    }

    public XStream getLibraryXStream() {
        return _libraryXStream;
    }

    public File getBaseLibraryDir() {
        File dir = new File(getWgaDataDir(), "lib");
        if (!dir.exists()) {
            dir.mkdirs();
        }
        return dir;
    }

    public boolean isLocalRequest(HttpServletRequest request) {

        try {
            // if servername is not loopback --> return false
            if (!InetAddress.getByName(request.getServerName()).isLoopbackAddress()) {
                return false;
            }
        } catch (UnknownHostException e1) {
            // might happen if localhost cannot be looked up
            // additional check for sure
            if (!request.getServerName().toLowerCase().equals("localhost")) {
                return false;
            }
        }

        try {
            // if remote address is not loopback -> return false
            if (!InetAddress.getByName(request.getRemoteAddr()).isLoopbackAddress()) {
                return false;
            }
        } catch (UnknownHostException e1) {
            // unparsable remote address - should not happen
            return false;
        }

        return true;

    }

    public void contextInitialized(ServletContextEvent arg0) {

        try {
            _context = arg0.getServletContext();

            // General inits
            this.instanceActiveSince = new Date();
            arg0.getServletContext().setAttribute(ATTRIB_CORE, this);
            WGFactory.setAuthModuleFactory(new WGAAuthModuleFactory(this));
            WGFactory.setMimetypeDeterminationService(new WGAMimetypeDeterminationService());

            // Create temp dir
            File mainTempDir = new File(System.getProperty("java.io.tmpdir"));
            this.wgaTempDir = new File(mainTempDir, ".wgatemp");
            if (wgaTempDir.exists()) {
                killTempDir(wgaTempDir);
            }
            wgaTempDir.mkdir();
            WGFactory.setTempDir(wgaTempDir);
            TemporaryFile.prepareDirectory(wgaTempDir);

            // Creating logger
            initLoggingFile();
            String debug = System.getProperty(SYSPROPERTY_DEBUG);
            if (debug != null) {
                for (String target : WGUtils.deserializeCollection(debug, ",", true)) {
                    Logger logger = Logger.getLogger(target);
                    logger.setLevel(Level.DEBUG);
                }
            }

            // Try to retrieve build properties
            _buildSignature = "NOSIGNATURE";
            try {
                InputStream buildPropIn = _context.getResourceAsStream("/WEB-INF/wgabuild.properties");
                Properties props = new Properties();
                props.load(buildPropIn);
                if (props.containsKey("signature")) {
                    _buildSignature = props.getProperty("signature");
                }
            } catch (RuntimeException e1) {
            }

            // Retrieving platform info
            String endDate = (new SimpleDateFormat("yyyy")).format(new Date());
            log.info(getReleaseString() + " (c) 2001-" + endDate + " Innovation Gate GmbH");
            try {
                this.servletPlatform = new Double(arg0.getServletContext().getMajorVersion() + "."
                        + arg0.getServletContext().getMinorVersion()).doubleValue();
                this.jspPlatform = new Double(
                        javax.servlet.jsp.JspFactory.getDefaultFactory().getEngineInfo().getSpecificationVersion())
                                .doubleValue();
                log.info("On platform " + arg0.getServletContext().getServerInfo() + " (Servlet Engine "
                        + this.servletPlatform + ", JSP Engine " + this.jspPlatform + ")");
            } catch (Exception ecx) {
                log.warn("Unable to retrieve platform info. Assuming Servlet Engine 3.0, JSP Engine 2.2", ecx);
                this.servletPlatform = 3.0;
                this.jspPlatform = 2.2;
            }
            arg0.getServletContext().setAttribute(WGACore.ATTRIB_SERVLET_ENGINE, new Double(this.servletPlatform));
            arg0.getServletContext().setAttribute(WGACore.ATTRIB_JSP_ENGINE, new Double(this.jspPlatform));

            // Starting other non-servlet services
            this._deployer = new WGPDeployer(this);

            // Create the statistics object
            _usageStatistics = new WGAUsageStatistics(this);

            // Test some neccessary running conditions for WGA

            // We test for exploded WAR
            if (getServletContext().getRealPath("/") == null) {
                log.fatal(
                        "WGA is not deployed as exploded WGA archive which is absolutely mandatory! WebTML deploying and rendering will not work correctly.");
            }

            // Eventually overwrite wga configpath/datapath/permanent log sysvar with init parameter
            String initConfigPath = getServletContext().getInitParameter(SYSPROPERTY_CONFIGPATH);
            if (initConfigPath != null) {
                log.info("Using WGA config path from servlet init parameter: " + initConfigPath);
                System.setProperty(SYSPROPERTY_CONFIGPATH, initConfigPath);
            }
            String initDataPath = getServletContext().getInitParameter(SYSPROPERTY_DATAPATH);
            if (initDataPath != null) {
                log.info("Using WGA data path from servlet init parameter: " + initDataPath);
                System.setProperty(SYSPROPERTY_DATAPATH, initDataPath);
            }
            String initPermLog = getServletContext().getInitParameter(SYSPROPERTY_LOGPATH);
            if (initPermLog != null) {
                log.info("Using WGA permanent log path from servlet init parameter: " + initPermLog);
                System.setProperty(SYSPROPERTY_LOGPATH, initPermLog);
            }

            // Do startup
            fireCoreEvent(new WGACoreEvent(WGACoreEvent.TYPE_PRE_STARTUP, null, this));
            startup();
        } catch (Throwable e) {
            log.fatal("Fatal error initializing wga", e);
            e.printStackTrace();
        }

    }

    public ServletContext getServletContext() {
        return _context;
    }

    @SuppressWarnings("unused")
    public static String getReleaseString() {

        StringBuffer output = new StringBuffer();

        output.append(WGAVersion.WGAPUBLISHER_PRODUCT_NAME).append(" ");
        output.append(WGAVersion.WGAPUBLISHER_MAJOR_VERSION).append(".")
                .append(WGAVersion.WGAPUBLISHER_MINOR_VERSION);

        if (WGAVersion.WGAPUBLISHER_MAINTENANCE_VERSION > 0) {
            output.append(".").append(WGAVersion.WGAPUBLISHER_MAINTENANCE_VERSION);
        }

        if (WGAVersion.WGAPUBLISHER_MAINTENANCE_VERSION > 0) {
            output.append(" Maintenance Release");
        } else {
            output.append(" Release");
        }

        if (WGAVersion.WGAPUBLISHER_PATCH_VERSION > 0) {
            output.append(" Patch " + WGAVersion.WGAPUBLISHER_PATCH_VERSION);
        }

        output.append(" (Build ").append(WGAVersion.WGAPUBLISHER_BUILD_VERSION).append(")");

        return output.toString();
    }

    public static String getGeneratorString() {

        StringBuffer output = new StringBuffer();

        output.append(WGAVersion.WGAPUBLISHER_PRODUCT_NAME).append(" ");
        output.append(WGAVersion.WGAPUBLISHER_MAJOR_VERSION).append(" ");
        if (!WGUtils.isEmpty(WGAVersion.WGAPUBLISHER_PLATFORM_NAME)) {
            output.append("\'").append(WGAVersion.WGAPUBLISHER_PLATFORM_NAME).append("\' Platform");
        }

        return output.toString();

    }

    public static String getPlatformString() {

        StringBuffer output = new StringBuffer();

        output.append(WGAVersion.WGAPUBLISHER_MAJOR_VERSION).append(".")
                .append(WGAVersion.WGAPUBLISHER_MINOR_VERSION).append(" ");
        output.append(WGAVersion.WGAPUBLISHER_PLATFORM_NAME);

        return output.toString();
    }

    // Platform info
    private double servletPlatform;

    private double jspPlatform;

    private AppLogAppender transientLogAppender = null;

    private File loggingDir = null;

    private AsyncAppender baseAppender = null;

    public void initLoggingFile() {

        // Base appender
        if (this.baseAppender == null) {
            this.baseAppender = new AsyncAppender();
            this.baseAppender.setBufferSize(1000);
            this.baseAppender.setBlocking(true);
            this.log.addAppender(this.baseAppender);
        }

        // Transient log
        if (this.transientLogAppender != null) {
            this.baseAppender.removeAppender(this.transientLogAppender);
            this.transientLogAppender.close();
            this.transientLogAppender = null;
        }
        if (this.loggingDir != null && this.loggingDir.exists()) {
            WGUtils.delTree(loggingDir);
        }

        this.loggingDir = new File(getWgaTempDir(), "applog");
        this.transientLogAppender = new AppLogAppender(loggingDir.getAbsolutePath(), "wga-", ".log");
        transientLogAppender.setName("wga.transientLog");
        transientLogAppender.setAppend(true);
        transientLogAppender.setEncoding("UTF-8");
        transientLogAppender.setLayout(LAYOUT_APPLOG);
        transientLogAppender.setMaxFileSize(1024 * 1024 * 10);
        transientLogAppender.setFilesToKeep(10);
        this.baseAppender.addAppender(transientLogAppender);

        // Permanent log
        String permanentLogPath = System.getProperty(SYSPROPERTY_LOGPATH);
        if (permanentLogPath != null) {
            File logPathFile = new File(permanentLogPath);
            if (!logPathFile.exists() || !logPathFile.isDirectory()) {
                log.error("Unable to use path for permanent log '" + permanentLogPath
                        + "' because it either does not exist or is no directory.");
            } else {
                log.info("Permanent application log stored in directory " + permanentLogPath);
                DatedFileAppender permanentFileAppender = new DatedFileAppender(permanentLogPath, "wga-", ".log");
                permanentFileAppender.setName("wga.permanentLog");
                permanentFileAppender.setAppend(true);
                permanentFileAppender.setEncoding("UTF-8");
                permanentFileAppender.setLayout(LAYOUT_APPLOG);
                this.baseAppender.addAppender(permanentFileAppender);
            }
        }
    }

    /**
     * @return
     */
    public File getWgaTempDir() {
        return wgaTempDir;
    }

    /*
     * (Kein Javadoc)
     * 
     * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent)
     */
    public void contextDestroyed(ServletContextEvent arg0) {

        try {
            shutdown();
            WGFactory.getInstance().shutdown();
        } catch (Throwable t) {
            log.error("Error shutting down WGA", t);
        } finally {
            if (log != null) {
                log.removeAllAppenders();
            }
            if (transientLogAppender != null) {
                transientLogAppender.close();
            }
            if (wgaTempDir != null) {
                killTempDir(wgaTempDir);
                wgaTempDir = null;
            }
            // B000048C2 - cleanup temp. plugin resources
            if (pluginSet != null) {
                pluginSet.clearTempResources();
                pluginSet = null;
            }
        }

    }

    public void shutdown() {

        logCategoryInfo(WGAVersion.WGAPUBLISHER_PRODUCT_NAME + " starting shutdown...", 1);

        fireCoreEvent(new WGACoreEvent(WGACoreEvent.TYPE_PRE_SHUTDOWN, null, this));

        logCategoryInfo("Disabling dispatcher", 2);

        WGPDispatcher dispatcher = getDispatcher();
        if (dispatcher != null) {
            dispatcher.setServePages(false);
        }

        // Clear listeners whose actions are no longer needed on shutdown
        _moduleRegistry.clearChangeListeners();

        // Shutdown various platform services (db-independent)
        logCategoryInfo("Shutting down platform services", 2);
        getLog().info("Shutting down scheduler");
        try {
            _quartzScheduler.shutdown(true);
        } catch (SchedulerException e) {
            getLog().error("Exception shutting down WGA scheduler", e);
        }
        _quartzScheduler = null;

        if (this.timer != null) {
            this.timer.shutdown();
            this.timer = null;
        }

        getLog().info("Shutting down lucene");
        if (this.luceneManager != null) {
            this.luceneManager.destroy();
            this.luceneManager = null;
        }

        this.fileDerivateManager.stop();
        this.fileDerivateManager = null;

        getLog().info("Shutting down WebSocket connections");
        _pageConnectionManager.shutdown();
        _independentWebSocketManager.shutdown();

        getLog().info("Shutting down event manager");
        _eventManager.shutdown();

        if (_clusterService != null) {
            _clusterService.shutdown();
            _clusterService = null;
        }

        if (_externalFileMaintenanceTask != null) {
            _externalFileMaintenanceTask.stop();
            _externalFileMaintenanceTask = null;
        }

        // Stop the event thread
        getLog().info("Shutting down event thread");
        WGFactory.getInstance().getEventThread().stop();

        // fire pre disconnect event
        fireCoreEvent(new WGACoreEvent(WGACoreEvent.TYPE_SHUTDOWN_PRE_DISCONNECT, null, this));

        logCategoryInfo("Closing content databases", 2);

        // Close content databases
        Iterator<String> dbs = new HashSet<String>(this.contentdbs.keySet()).iterator();
        while (dbs.hasNext()) {
            removeContentDB(dbs.next());
        }

        logCategoryInfo("Closing personalisation databases", 2);

        // Close personalisation databases
        dbs = new HashSet<String>(this.personalisationdbs.keySet()).iterator();
        while (dbs.hasNext()) {
            removePersonalisationDB(dbs.next());
        }

        logCategoryInfo("Closing domains", 2);

        // Close domains (especially their auth modules)
        closeDomainConfigs(this.domains);

        logCategoryInfo("Shutting down basic services", 2);

        unDeployErrorPage();

        WGHierarchicalDatabase.removeCoreListener(_hdbCoreListener);

        // fire post disconnect event
        fireCoreEvent(new WGACoreEvent(WGACoreEvent.TYPE_SHUTDOWN_POST_DISCONNECT, null, this));

        if (_httpSessionManager != null) {
            _httpSessionManager.clearListeners();
            _httpSessionManager.shutdown();
        }

        // Close integrated JMX
        _jmx.shutdown();
        _jmx = null;

        // Close access logger
        if (_accessLogger != null) {
            _accessLogger.close();
        }

        // Cleanup deployer
        _deployer.shutdown();

        // Cleanup WebTML cache
        try {
            _webTMLCache.close();
            _webTMLCache = null;
        } catch (CacheException e) {
            getLog().error("Exception shutting down WebTML cache", e);
        }

        // Cleanup statistics
        if (_usageStatistics != null) {
            _usageStatistics.dispose();
            _usageStatistics = null;
        }

        // Remove all core listeners
        coreEventListeners.clear();

        // Close expression engines and WGA classpath
        ExpressionEngineFactory.closeEngines();
        libraryClassLoadingChain = null;

        // Clear and close caches
        WGFactory.getAuthModuleFactory().clearCache();
        try {
            designFileCache.destroy();
            designFileCache = null;
        } catch (CacheException e) {
            log.error("Exception closing design file cache", e);
        }

        try {
            _calledSequenceIds.destroy();
            _calledSequenceIds = null;
        } catch (CacheException e) {
            log.error("Exception closing action sequence id cache", e);
        }
        EHCacheCore.getCacheManager().shutdown();

        // Unregister HSQLDB driver
        try {
            DriverManager.deregisterDriver(DriverManager.getDriver("jdbc:hsqldb:mem:justShuttingDown"));
        } catch (SQLException e1) {
            getLog().error("Exception de-registering HSQL driver", e1);
        }

        // Close logserver
        if (_logServer != null) {
            try {
                _logServer.shutdown();
            } catch (IOException e) {
                getLog().error("Exception shutting down WGA Remote Log Server", e);
            }
            _logServer = null;
        }

        _problemRegistry.close();
        _problemRegistry = null;

        TemporaryFile.stopEviction();

        fireCoreEvent(new WGACoreEvent(WGACoreEvent.TYPE_POST_SHUTDOWN, null, this));
        logCategoryInfo(WGAVersion.WGAPUBLISHER_PRODUCT_NAME + " finished shutdown", 1);
    }

    // Application attributes set by this program
    public static final String ATTRIB_CORE = "Core";

    public static final String ATTRIB_DISPATCHER = "Dispatcher";

    public static final String ATTRIB_MAINCONTEXT = "TMLContext";

    public static final String ATTRIB_CONTENTDBS = "Databases";

    public static final String ATTRIB_WGPPATH = "WGPPath";

    public static final String ATTRIB_FETCHED_USERPROFILES = "FetchedUserProfiles";

    public static final String ATTRIB_SERVLET_ENGINE = "ServletEngine";

    public static final String ATTRIB_JSP_ENGINE = "JspEngine";

    public static final String ATTRIB_TAGIDS = "TagIds";

    public static final String ATTRIB_REQUESTURL = "RequestURL";

    public static final String ATTRIB_MEDIAKEY = "MediaKey";

    public static final String ATTRIB_MIMETYPE = "MimeType";

    public static final String ATTRIB_BROWSERINTERFACE = "BrowserInterface";

    public static final String ATTRIB_NO_CONTENT_NOTIFCATION_URL = "de.innovationgate.wgpublisher.NoContentNotificationURL";

    public static final String ATTRIB_VIRTUAL_CONTENT_URL = "de.innovationgate.wgpublisher.VirtualContentURL";

    public static final String ATTRIB_EXCEPTION = "Exception";

    public static final String ATTRIB_WGAERROR = "WGAError";

    public static final String ATTRIB_OUTER_DESIGN = "OuterDesign";
    public static final String ATTRIB_OUTER_DESIGN_DB = "OuterDesignDb";

    public static final String ATTRIB_ROOT_TAG = "RootTag";

    public static final String ATTRIB_LOGINERROR = "LoginError";

    public static final String ATTRIB_SERVLETRESPONSE = "OutputStream";

    public static final String ATTRIB_REDIRECT = "Redirect";

    public static final String ATTRIB_REQUESTTIME = "RequestTime";

    public static final String ATTRIB_SCOPEOBJECTREGISTRY = "ScopeObjectRegistry";

    public static final String SESSION_ACTIONS = "Actions";

    public static final String SESSION_SCOPEOBJECTREGISTRY_BASE = "ScopeObjectRegistry_";

    public static final String SESSION_ACTION_SEQUENCE = "ActionSequence";

    public static final String SESSION_ADMINNAME = "AdminName";

    public static final String SESSION_ADMINPASSWORD = "AdminPassword";

    public static final String SESSION_WARNED_PROFILESESSIONS = "WarnedProfileSessions";

    public static final String ATTRIB_TMLFORM = "TMLForm";

    public static final String ATTRIB_POSTED_TMLFORM = "PostedTMLForm";

    public static final String ATTRIB_LASTFORM = "LastForm";

    /**
     * Request Variable. When set to true allows OpenWGA to use regular user/password login although the auth module is request based
     * May be invalidated by using #ATTRIB_FORCEREQUESTBASELOGIN
     */
    public static final String ATTRIB_FORCEREGULARLOGIN = "ForceRegularLogin";

    /**
     * Request Variable. When set to true will force OpenWGA to use request based login, even when {@link #ATTRIB_FORCEREGULARLOGIN} is set to true.
     */
    public static final String ATTRIB_FORCEREQUESTBASELOGIN = "ForceRequestBasedLogin";

    public static final String ATTRIB_REQUESTPATH = "RequestPath";

    // Database attributes set by this program
    public static final String DBATTRIB_DBKEY = "DBURLPath";

    public static final String DBATTRIB_EXPRESSION_DEFAULT = "ExpressionDefault";

    public static final String DBATTRIB_QUERY_DEFAULT = "QueryDefault";

    public static final String DBATTRIB_DEFAULT_MEDIAKEY = "DefaultMediaKey";

    public static final String DBATTRIB_ALLOW_BROWSING = "AllowBrowsing";
    public static final String DBATTRIB_BROWSING_SECURITY = PublisherOption.OPTION_BROWSING_SECURITY;

    public static final String DBATTRIB_STARTPAGE = "StartPage";

    /**
     * Boolean publisher option that identifies a database as authoring application
     * WGA will restrict access to this database to a configured port or disable it et al
     * if authoring is disabled by configuration. 
     */
    public static final String DBATTRIB_AUTHORING_APP = "AuthoringApp";

    /**
     * Boolean publisher option that identifies a database as admininstrative application
     * WGA will restrict access to this database to a configured port and will not allow access
     * if someone is not logged in as OpenWGA admin
     */
    public static final String DBATTRIB_ADMIN_APP = "AdminApp";

    public static final String DBATTRIB_MAXQUERYRESULTS = "MaxQueryResults";

    public static final String DBATTRIB_DOMAIN = "Domain";

    public static final String DBATTRIB_STORED_QUERIES = "StoredQueries";

    public static final String DBATTRIB_META_MAPPINGS = "MetaMappings";

    public static final String DBATTRIB_ITEM_MAPPINGS = "ItemMappings";
    public static final String DBATTRIB_PLUGIN_SHORTCUTS = "PluginShortcuts";

    public static final String DBATTRIB_HOME_PAGE = "HomePage";

    public static final String DBATTRIB_LOGIN_PAGE = "LoginPage";

    public static final String DBATTRIB_PERSMODE = "PersMode";

    public static final String DBATTRIB_PERSMODE_OPT_IN = "PersMode.OptIn";

    public static final String DBATTRIB_PORTLETREGISTRYMODE = "PortletRegistryMode";

    public static final String DBATTRIB_PERSSTATMODE = "PersStatMode";

    public static final String DBATTRIB_FILEEXPIRATION_MINUTES = "FileExpirationMinutes";

    public static final String DBATTRIB_FILECACHE = "FileCache";

    public static final String DBATTRIB_FILECACHE_ENTRIES = "FileCacheEntries";

    public static final String DBATTRIB_FILECACHE_THRESHOLD = "FileCacheThreshold";

    public static final String DBATTRIB_PPRCACHE = "PPRCache";

    public static final String DBATTRIB_PPRCACHE_ENTRIES = "PPRCacheEntries";

    public static final String ATTRIB_BI_COLLECTIONS_SHOW_RELEASED_ONLY = "BICollectionsShowReleasedOnly";

    public static final String DBATTRIB_VARPROVISIONING = "VarProvisioning";

    public static final String DBATTRIB_TITLEPATHURL = "TitlePathURL";

    public static final String DBATTRIB_TITLEPATHURL_CONTENTINDEXING = "TitlePathURL.ContentIndexing";

    public static final String DBATTRIB_TITLEPATHURL_MIXEDLANGUAGES = "TitlePathURL.MixedLanguages";

    public static final String DBATTRIB_TITLEPATHURL_INCLUDEKEYS = "TitlePathURL.IncludeKeys";
    public static final String DBATTRIB_TITLEPATHURL_USESTRUCTKEYS = "TitlePathURL.UseStructkeysAsKey";

    public static final String DBATTRIB_TITLEPATHURL_ALLOW_UMLAUTE = "TitlePathURL.AllowUmlaute";

    public static final String DBATTRIB_TITLEPATHMANAGER = "TitlePathManager";

    public static final String DBATTRIB_WEBTMLCACHE_SERVESTALEDATA = "WebTMLCache.ServeStaleData";

    public static final String DBATTRIB_DEFAULT_ITEM_ENCODING = "DefaultItemEncoding";

    public static final String DBATTRIB_USERCLASSES = "UserClasses";

    public static final String DBATTRIB_RESOURCEBUNDLE_MANAGER = "ResourceBundleManager";

    public static final String DBATTRIB_HTTPLOGIN = "HttpLogin";

    public static final String DBATTRIB_LUCENE_CONFIG = "LuceneConfig";

    // contains a list of IPv4Restrictions for a db
    public static final String DBATTRIB_CLIENTRESTRICTIONS = "ClientRestrictions";

    /**
     * Boolean Attrib determines if the database is allowed to use remote docs
     */
    public static final String DBATTRIB_USEREMOTECS = "UseRemoteContentStores";

    /**
     * Attribute to determine the language behaviour for a database, influencing
     * the way the preferred language is determined 
     */
    public static final String DBATTRIB_LANGUAGEBEHAVIOUR = "LanguageBehaviour";

    /**
     * Boolean attrib determines if contents from this db may be used for remote
     * docs
     */
    public static final String DBATTRIB_ISREMOTECS = "IsRemoteContentStore";

    /**
     * Configures the file annotators to be active on this database 
     */
    public static final String DBATTRIB_FILE_ANNOTATORS = "FileAnnotators";

    // System properties
    /**
     * Directory from where to load wga configuration, defaults to the user's home directory. Many other locations are used relative to this path by default.
     */
    public static final String SYSPROPERTY_CONFIGPATH = "de.innovationgate.wga.configpath";

    /**
     * Directory where WGA can store management data, defaults to CONFIPATH/wgadata
     */
    public static final String SYSPROPERTY_DATAPATH = "de.innovationgate.wga.datapath";

    /**
     * Name of the WGA configuration file, defaults to wga.xml
     */
    public static final String SYSPROPERTY_CONFIGFILE = "de.innovationgate.wga.configfile";

    /**
     * Can be used to set one log4j logging path to DEBUG level
     */
    /**
     * Can be used to set one log4j logging path to DEBUG level
     */
    public static final String SYSPROPERTY_DEBUG = "de.innovationgate.wga.debug";

    private static final String OLD_CONFIGFILE_NAME = "wga.xml";

    private static final String CONFIGFILE_NAME = "wgaconfig.xml";

    public static final java.text.DateFormat DATEFORMAT_GMT = new java.text.SimpleDateFormat(
            "EEE, dd MMM yyyy HH:mm:ss z", new Locale("en", "US"));

    static Logger log = Logger.getLogger("wga");

    private Date instanceActiveSince;

    // domain configurations mapped by domainname
    private Map<String, WGADomain> domains;

    private org.quartz.Scheduler _quartzScheduler;

    private ArrayList<Object> customCoreListeners;

    private SystemContainerManager _systemContainerManager;

    private File _wgaDataDir;

    WGAConfiguration _wgaConfiguration;

    private WGADesignManager _designManager;

    private PersonalisationManager _persManager;

    public PersonalisationManager getPersManager() {
        return _persManager;
    }

    private ModuleRegistry _moduleRegistry = null;

    private WGALoggerWrapper _accessLogger;

    private WGHierarchicalDatabaseCoreListener _hdbCoreListener;

    private ExternalFileServingMaintenanceTask _externalFileMaintenanceTask;

    private WGAConfigurationOptionReader _servicesServerOptionReader;

    private JMX _jmx;

    private ProblemRegistry _problemRegistry;

    private XStream _defaultSerializer;

    private WGACoreTimer timer;

    private SymmetricEncryptionEngine _symmetricEncryptionEngine;

    public ProblemRegistry getProblemRegistry() {
        return _problemRegistry;
    }

    public ModuleRegistry getModuleRegistry() {
        return _moduleRegistry;
    }

    private static final String SYSPROPERTY_QUARTZ_THREADCOUNT = "de.innovationgate.wga.quartz.threadcount";

    private static final String SYSPROPERTY_QUARTZ_THREADPRIORITY = "de.innovationgate.wga.quartz.threadpriority";

    private static final String DEFAULT_CONFIG_DIR = "WGA";

    public void startup() throws ServletException {
        try {
            this.log.info("Starting up " + WGABrand.getName() + " service");

            // prepare HDB
            TMLScriptHDBListenerFactory listenerFactory = new TMLScriptHDBListenerFactory(this);
            WGHierarchicalDatabase.setDefaultListenerFactory(listenerFactory);
            WGHierarchicalDatabase.setDefaultStartupImpl(null);
            _hdbCoreListener = new WGHierarchicalDatabaseCoreListener() {

                public void databaseCreated(WGHierarchicalDatabase hdb) {
                    // Eventually load and initialize model
                    WGDatabase db = hdb.getWrappedDB();
                    try {
                        HDBModel.createModelObject(WGACore.this, db);
                    } catch (Exception e) {
                        WGACore.this.log.error("Error initializing HDB model for database " + db.getDbReference(),
                                e);
                    }
                }

                public void databaseRemoved(WGHierarchicalDatabase hdb) {
                }

            };
            WGHierarchicalDatabase.addCoreListener(_hdbCoreListener);

            String configFilePath = retrieveConfigPath();
            // init DES-Encrypter
            File desKeyFile = new File(configFilePath, "des.key");
            desEncrypter = new DESEncrypter();
            try {
                try {
                    desEncrypter.init(desKeyFile);
                    log.info("DESEncrypter initialized using keyfile '" + desKeyFile.getPath() + "'.");
                } catch (DESEncrypter.PersistentKeyException e) {
                    log.warn(
                            "Unable to create or restore encryption key - generating temporary key. Session replication will not work with this key. Ensure the application server has read/write access to '"
                                    + desKeyFile.getPath() + "'.",
                            e);
                    // init with temp key
                    desEncrypter.init();
                    log.info("DESEncrypter initialized with temporary key.");
                }
            } catch (GeneralSecurityException e) {
                // VM does not support 'des' algorithm - should not happen in
                // wga supported VMs
                log.error("Unable to create DESEncrypter.", e);
                throw new ServletException("wga publisher initialization failure");
            }

            // init symmetric encryption engine
            File keyFile = new File(configFilePath, "openwga.key");
            _symmetricEncryptionEngine = new SymmetricEncryptionEngine();
            try {
                byte[] keyBytes = null;
                if (!keyFile.exists()) {
                    log.info("SymmetricEncryptionEngine: Generating new key file: '" + keyFile.getAbsolutePath()
                            + "'.");
                    SecretKey key = _symmetricEncryptionEngine.generateKey();
                    keyBytes = key.getEncoded();
                    FileOutputStream keyOut = new FileOutputStream(keyFile);
                    keyOut.write(keyBytes);
                    keyOut.close();
                } else {
                    log.info("SymmetricEncryptionEngine: Using keyfile '" + keyFile.getAbsolutePath() + "'.");
                    FileInputStream keyIn = new FileInputStream(keyFile);
                    ByteArrayOutputStream keyOut = new ByteArrayOutputStream();
                    WGUtils.inToOut(keyIn, keyOut, 128);
                    keyIn.close();
                    keyBytes = keyOut.toByteArray();
                }
                _symmetricEncryptionEngine.init(keyBytes);
            } catch (Exception e) {
                log.error("Unable to init symmetric encryption engine.", e);
                throw new ServletException("Unable to init symmetric encryption engine", e);
            }

            // get config xml document
            boolean configMigrated = false;
            this.configFile = retrieveConfigFile();

            if (!configFile.exists()) {
                // no new style wga configuration - check if we have to migrate an old one
                File oldConfigFile = retrieveOldConfigFile();
                if (oldConfigFile.exists()) {
                    migrateWGAConfiguration(oldConfigFile, configFile);
                    configMigrated = true;
                } else {
                    // no previous old style config - create default config
                    createDefaultWGAConfiguration(configFile);
                }
            }

            log.info("Using config file: " + configFile.getAbsolutePath());
            this.configFileLastModified = this.configFile.lastModified();
            parseConfigFile();
            adaptWGAConfigurationToVersion();

            initQuartz();
            _deployer.startup();
            _calledSequenceIds = CacheFactory.createCache("WGACore_calledSequenceIds", 10000, null);

            String dataPath = System.getProperty(SYSPROPERTY_DATAPATH);
            if (dataPath != null) {
                _wgaDataDir = new File(dataPath);
            } else {
                _wgaDataDir = new File(configFile.getParent(), "wgadata");
            }

            if (!_wgaDataDir.exists()) {
                if (!_wgaDataDir.mkdir()) {
                    log.error("Unable to create WGA data directory '" + _wgaDataDir.getPath()
                            + "'. Some WGA functionalities that rely on this will not work!");
                    _wgaDataDir = null;

                }
            } else if (!_wgaDataDir.isDirectory()) {
                log.error("Unable to create WGA data directory '" + _wgaDataDir.getPath()
                        + "' because some other file uses the same name. Some WGA functionalities that rely on this will not work!");
                _wgaDataDir = null;
            }

            String hsqlRoot = System
                    .getProperty(de.innovationgate.webgate.api.hsql.WGDatabaseImpl.SYSPROPERTY_HSQL_ROOT);
            if (WGUtils.isEmpty(hsqlRoot)) {
                log.info("Setting root directory for embedded HSQLDB databases to config dir: "
                        + configFile.getParentFile().getAbsolutePath());
                System.setProperty(de.innovationgate.webgate.api.hsql.WGDatabaseImpl.SYSPROPERTY_HSQL_ROOT,
                        configFile.getParentFile().getAbsolutePath());
            } else {
                log.info("Root directory for embedded HSQLDB databases is: " + hsqlRoot);
            }

            String authFileRoot = System.getProperty(FileAuthenticationModule.SYSPROPERTY_AUTH_FOLDER);
            if (WGUtils.isEmpty(authFileRoot)) {
                log.info("Setting root directory for XML authentication files to config dir: "
                        + configFile.getParentFile().getAbsolutePath());
                System.setProperty(FileAuthenticationModule.SYSPROPERTY_AUTH_FOLDER,
                        configFile.getParentFile().getAbsolutePath());
            } else {
                log.info("Root directory for XML authentication files is: " + authFileRoot);
            }

            // Prepare problem registry
            _problemRegistry = new ProblemRegistry(this);
            ProblemOccasion occ = new UpdateConfigOccasion();
            getProblemRegistry().clearProblemOccasion(occ);

            // init login bruteForceLoginBlocker
            bruteForceLoginBlocker = new BruteForceLoginBlocker(this);

            // retrieve general configuration
            initReadGeneralConfig(false);

            // Init some managers
            _systemContainerManager = new SystemContainerManager(this);
            _pageConnectionManager = new PageConnectionManager();
            _independentWebSocketManager = new IndependentWebSocketManager();
            _eventManager = new EventManager(this);
            _persManager = new PersonalisationManager(this);

            // Retrieve media key and element mappings
            initReadMappings();

            // Create expression engines
            ExpressionEngineFactory.createEngines(this);

            // Init default serializer
            initDefaultSerializer();

            // Initialize custom core listeners
            initCustomCoreListeners();

            // Read domain configurations
            Map<String, WGADomain> newDomainConfigs = initReadDomains();

            deployErrorPage();

            // Init scheduler before db connection, so system containers in dbs can add jobs
            _scheduler = new Scheduler(this);

            // Init module registry
            initModuleRegistry();

            // Init JMX
            _jmx = new JMX(this);

            // fire pre connect event
            fireCoreEvent(new WGACoreEvent(WGACoreEvent.TYPE_STARTUP_PRE_CONNECT, null, this));

            // Create server option readers
            _variousServerOptionReader = getConfigOptionReader(new OptionFetcher() {
                @Override
                public Map<String, String> fetch(WGAConfiguration config) {
                    return config.getServerOptions();
                }
            }, WGAServerOptionsModuleType.class, VariousOptionsCollector.class);

            _servicesServerOptionReader = getConfigOptionReader(new OptionFetcher() {
                @Override
                public Map<String, String> fetch(WGAConfiguration config) {
                    return config.getServerOptions();
                }
            }, WGAServerOptionsModuleType.class, ServicesCollector.class);

            // connect plugins
            updatePlugins(newDomainConfigs);

            // Log most important available modules
            logCategoryInfo("Modules", 1);
            logModuleRegistry();

            // Some tasks that adapt options in registry to those configured in the WGA configuration
            adaptConfigurationToRegistry(configMigrated);

            // init cluster service
            initClusterService(null);

            // init session manager
            initHttpSessionManager(null);

            // Init event manager
            _eventManager.reloadConfig();

            // Init filter mappings (which may be feeded from registry)
            initReadFilterMappings();

            // Init access logger - Must be after modreg so JDBC drivers from mod dependencies are already loaded
            initAccessLogger();

            // Init file derivate manager
            this.fileDerivateManager.init(_wgaConfiguration);

            // open database servers (must be before domains, to allow pers db connections)
            updateDatabaseServers();

            // Startup domain configurations (must be after plugin connection so that domains can use plugin-provided functionalities)
            initStartupDomains(newDomainConfigs);

            // Init design manager
            logCategoryInfo("Design Sources", 1);
            _designManager = new WGADesignManager(this,
                    _wgaConfiguration.getDesignConfiguration().getDesignSources());

            // Init mail service
            WGFactory.setMailService(new WGAMailService(this));

            logCategoryInfo("Workflow engine", 1);
            Class defaultWorkflowEngine = WGFactory.getDefaultWorkflowEngine();
            ModuleDefinition wfDef = getModuleRegistry().getModuleDefinition(WorkflowEngineModuleType.class,
                    defaultWorkflowEngine);
            if (wfDef != null) {
                getLog().info("Default workflow engine is: " + wfDef.getTitle(Locale.ENGLISH));
            } else {
                getLog().info("Default workflow engine is: " + defaultWorkflowEngine.getName()
                        + " (custom unregistered engine)");
            }

            // open content databases
            updateContentDBs();

            // fire post connect event
            fireCoreEvent(new WGACoreEvent(WGACoreEvent.TYPE_STARTUP_POST_CONNECT, null, this));

            // Load scheduler jobs after db connection, so they can refer the connected dbs
            updateScheduler();

            // Init shares
            updateShares();

            this.getServletContext().setAttribute(WGACore.ATTRIB_CONTENTDBS, this.contentdbs);

            // init TestCore
            initTestCore();

            /*
            // Perform initial daily DB maintenance if in devmode, because devmode servers normally do not run 24 hours
            if ("true".equals(System.getProperty(SYSPROPERTY_DEVELOPMENT_MODE))) {
            performDbMaintenanceForDevmode();
            }
            */

            // notify LuceneManger
            logCategoryInfo("Lucene Fulltext Index", 1);
            if (luceneManager != null) {
                luceneManager.startup();
            }

            initExternalFileServing();

            // start external file serving maintenance
            _externalFileMaintenanceTask = new ExternalFileServingMaintenanceTask(this);
            _externalFileMaintenanceTask.start();

            // Setup integrated JMX server
            _jmx.setup();

            // Start timer tasks
            this.timer = new WGACoreTimer(this);

            // Enable daily db backend maintenance if applicable
            if (isRunSingleNodeFunctionalities()) {
                WGFactory.getInstance().setDatabaseBackendMaintenanceEnabled(true);
            }

            // Init finished
            logCategoryInfo(WGAVersion.WGAPUBLISHER_PRODUCT_NAME + " ready", 1);
            WGFactory.getInstance().closeSessions();
            fireCoreEvent(new WGACoreEvent(WGACoreEvent.TYPE_ONLINE, null, this));

        } catch (Exception exc) {
            log.fatal("Fatal error initializing WGA", exc);
            throw new ServletException("Servlet initialization failure", exc);

        } catch (Error err) {
            log.fatal("Fatal error initializing WGA", err);
            throw new ServletException("Servlet initialization failure", err);
        } finally {
            WGPDispatcher dispatcher = getDispatcher();
            if (dispatcher != null) {
                dispatcher.setServePages(true);
            }
        }
    }

    public void performDbMaintenanceForDevmode() throws WGAPIException {
        logCategoryInfo("Database maintenance", 1);
        Thread maintenance = new Thread() {

            @Override
            public void run() {

                for (WGDatabase db : getContentdbs().values()) {
                    try {
                        if (db.getContentStoreVersion() < WGDatabase.CSVERSION_WGA5) {
                            continue;
                        }

                        db.openSession();
                        if (db.isBackendServiceSupported(WGDatabase.BACKENDSERVICE_DAILY_MAINTENANCE)) {
                            db.getSessionContext().setBatchProcess(true);
                            db.setCachingEnabled(true);
                            db.callBackendService(WGDatabase.BACKENDSERVICE_DAILY_MAINTENANCE, null);
                        }
                    } catch (Throwable e) {
                        getLog().error("Exception performing database maintenance on '" + db.getDbReference() + "'",
                                e);
                    } finally {
                        WGFactory.getInstance().closeSessions();
                    }
                }

            }

        };
        maintenance.start();
        try {
            maintenance.join();
        } catch (InterruptedException e) {
        }

    }

    public void initClusterService(WGAConfiguration oldConfig) {
        if (oldConfig == null
                || !getWgaConfiguration().getClusterConfiguration().equals(oldConfig.getClusterConfiguration())) {
            logCategoryInfo("Cluster Service", 1);
            if (_clusterService != null) {
                try {
                    getLog().info("Shutting down current cluster service instance to apply config changes.");
                    _clusterService.shutdown();
                    _clusterService = null;
                } catch (Throwable e) {
                    getLog().error("Failed to shutdown cluster service instance '"
                            + _clusterService.getClass().getName() + "'", e);
                }
            }

            // choose cluster service
            String implClass = getWgaConfiguration().getClusterConfiguration().getImplClassName();
            if (implClass != null && getWgaConfiguration().getClusterConfiguration().isEnabled()) {
                try {
                    Class<ClusterService> clusterServiceImplClass = (Class<ClusterService>) getLibraryLoader()
                            .loadClass(implClass);
                    Constructor<ClusterService> c = clusterServiceImplClass.getDeclaredConstructor(WGACore.class);
                    _clusterService = c.newInstance(this);
                } catch (Throwable e) {
                    getLog().error("Failed to create cluster service instance of type '" + implClass + "'", e);
                }
            }

            // nothing configured or impl class not found - fallback to single node cluster
            if (_clusterService == null) {
                _clusterService = new SingleNodeClusterService(this);
            }

            try {
                getLog().info(
                        "Starting cluster service implementation '" + _clusterService.getClass().getName() + "'");
                _clusterService.startup();
            } catch (Throwable e) {
                getLog().error("Startup of cluster service implementation '" + _clusterService.getClass().getName()
                        + "' failed.", e);
                // fallback to single node cluster if possible
                if (!_clusterService.getClass().equals(SingleNodeClusterService.class)) {
                    getLog().info("Falling back to default implementation '"
                            + SingleNodeClusterService.class.getName() + "'");
                    try {
                        _clusterService = new SingleNodeClusterService(this);
                        _clusterService.startup();
                    } catch (Throwable e1) {
                        getLog().error("Startup of cluster service implementation '"
                                + _clusterService.getClass().getName() + "' failed.", e1);
                    }
                }
            }
        }
    }

    private void initHttpSessionManager(WGAConfiguration oldConfig) {
        if (oldConfig == null || !getWgaConfiguration().getHttpSessionManagerConfiguration()
                .equals(oldConfig.getHttpSessionManagerConfiguration())) {
            logCategoryInfo("HTTP session Manager", 1);
            if (_httpSessionManager != null) {
                try {
                    getLog().info("Shutting down current HTTP session manager instance to apply config changes.");
                    _httpSessionManager.clearListeners();
                    _httpSessionManager.shutdown();
                    _httpSessionManager = null;
                } catch (Throwable e) {
                    getLog().error("Failed to shutdown HTTP session manager instance  '"
                            + _httpSessionManager.getClass().getName() + "'", e);
                    _httpSessionManager = null;
                }
            }

            String implClass = getWgaConfiguration().getHttpSessionManagerConfiguration().getImplClassName();
            if (implClass != null && getWgaConfiguration().getHttpSessionManagerConfiguration().isEnabled()) {
                try {
                    Class<AbstractWGAHttpSessionManager> managerImplClass = (Class<AbstractWGAHttpSessionManager>) getLibraryLoader()
                            .loadClass(implClass);
                    getLog().info("Initializing HTTP session manager '" + managerImplClass.getName() + "'.");
                    _httpSessionManager = managerImplClass.newInstance();
                    _httpSessionManager.setContext(_context);
                    _httpSessionManager.addListener(new WGAHttpSessionListener());
                    _httpSessionManager.startup(getWgaConfiguration().getHttpSessionManagerConfiguration());
                } catch (Throwable e) {
                    getLog().error("Failed to create HTTP session manager instance of type '" + implClass + "'", e);
                    _httpSessionManager = null;
                }
            }

            if (_httpSessionManager == null) {
                getLog().info("Using HTTP session management provided by servlet container");
            }
        }

    }

    private void initDefaultSerializer() {

        Dom4JDriver driver = new Dom4JDriver();
        OutputFormat format = OutputFormat.createCompactFormat();
        format.setSuppressDeclaration(true);
        driver.setOutputFormat(format);

        _defaultSerializer = new XStream(driver);
        _defaultSerializer.setClassLoader(getLibraryLoader());
        _defaultSerializer.alias("tmloption", TMLOption.class);
        _defaultSerializer.alias("version", Version.class);
        _defaultSerializer.alias("portletState", TMLPortletState.class);
        _defaultSerializer.registerConverter(ExpressionEngineFactory.getTMLScriptEngine());

        // DOM4J Objects
        _defaultSerializer.registerConverter(new SingleValueConverter() {

            public boolean canConvert(Class arg0) {
                return org.dom4j.Element.class.isAssignableFrom(arg0);
            }

            public Object fromString(String arg0) {
                try {
                    org.dom4j.Document doc = DocumentHelper.parseText(arg0);
                    return doc.getRootElement();
                } catch (DocumentException e) {
                    Logger.getLogger("wga.ajax")
                            .error("Exception deserializing DOM4J Element from TMLPortlet.SessionContext", e);
                    return "";
                }
            }

            public String toString(Object arg0) {

                Element elem = (Element) arg0;
                return elem.asXML();

            }

        });

        // Serialize TMLContext to their context path. All other info is dropped. Deserialize to a TMLContext.Serialized.
        _defaultSerializer.registerConverter(new SingleValueConverter() {

            @SuppressWarnings("rawtypes")
            @Override
            public boolean canConvert(Class arg0) {
                return TMLContext.class.isAssignableFrom(arg0);
            }

            @Override
            public String toString(Object arg0) {
                try {
                    TMLContext cx = (TMLContext) arg0;
                    return cx.getpath();
                } catch (WGAPIException e) {
                    throw new RuntimeException("Exception serializing TMLContext", e);
                }
            }

            @Override
            public Object fromString(String arg0) {
                return new TMLContext.Serialized(arg0);
            }

        });

        // Version object
        _defaultSerializer.registerConverter(new SingleValueConverter() {

            public boolean canConvert(Class arg0) {
                return Version.class.isAssignableFrom(arg0);
            }

            public Object fromString(String arg0) {
                return new Version(arg0);
            }

            public String toString(Object arg0) {

                return ((Version) arg0).toString();

            }

        });

        // GSON data structures
        _defaultSerializer.registerConverter(new SingleValueConverter() {

            private Gson _gson = new GsonBuilder().create();

            @Override
            public boolean canConvert(Class arg0) {
                return JsonElement.class.isAssignableFrom(arg0);
            }

            @Override
            public Object fromString(String arg0) {
                return new JsonParser().parse(arg0);
            }

            @Override
            public String toString(Object arg0) {
                return _gson.toJson((JsonElement) arg0);
            }

        });

        // Serialize types not meant to be serialized to empty string, deserialize to null
        _defaultSerializer.registerConverter(new SingleValueConverter() {

            public boolean canConvert(@SuppressWarnings("rawtypes") Class clazz) {
                return DEFAULT_SERIALIZER_NONSERIALIZABLE_TYPES.contains(clazz);
            }

            public Object fromString(String arg0) {
                return null;
            }

            public String toString(Object arg0) {
                return "";
            }
        });

    }

    /**
     * Do necessary updates to configuration when updating from an earlier version
     * @throws Exception 
     * @throws FileNotFoundException 
     */
    private void adaptWGAConfigurationToVersion() throws FileNotFoundException, Exception {

        Version configVersion = new Version(6, 0, 0);
        if (_wgaConfiguration.getWgaVersion() != null) {
            configVersion = new Version(_wgaConfiguration.getWgaVersion());
        }

        Version wgaVersion = WGAVersion.toCsConfigVersion();
        if (configVersion.equals(wgaVersion)) {
            return;
        }

        String hashingServiceOption = WGAConfiguration.SERVEROPTION_SERVICE_APIS_PREFIX
                + HashingSchemeType.class.getName();
        if (!configVersion.isAtLeast(6, 1)) {
            // Set hashing scheme for existing installations to SHA-1
            if (_wgaConfiguration.getServerOptions().get(hashingServiceOption) == null) {
                _wgaConfiguration.getServerOptions().put(hashingServiceOption, SHA1HashingScheme.class.getName());
            }
        }

        _wgaConfiguration.setWgaVersion(wgaVersion.toString());

    }

    private void registerMandatoryJobs(List<String> currentJobs) {

        if (isRunSingleNodeFunctionalities()) {
            JobSchedule pendingReleaseSchedule = new JobSchedule();
            pendingReleaseSchedule.setEnabled(true);
            pendingReleaseSchedule.setType(JobSchedule.TYPE_CRON);
            pendingReleaseSchedule.setScheduleData("0 0/1 * * * ?");
            try {
                Task task = new PendingReleaseTask();
                Job job = _scheduler.addCustomTaskJob(JOBNAME_PUBLISH_PENDING_RELEASE, task, false,
                        pendingReleaseSchedule);
                job.setQuiet(true);
                job.setDescription("Publishes documents that passed workflow and are now are pending release");
                job.setOrigin(Job.ORIGIN_WGACONFIG);
                currentJobs.add(job.getName());
            } catch (Exception e) {
                getLog().error("Unable to add pending release publishing task to wga scheduler.", e);
            }
        }

        if (getWgaConfiguration().getLuceneManagerConfiguration().isOptimizeIndexAutomatically()) {
            JobSchedule optimizeIndexSchedule = new JobSchedule();
            optimizeIndexSchedule.setEnabled(true);
            optimizeIndexSchedule.setType(JobSchedule.TYPE_CRON);
            optimizeIndexSchedule.setScheduleData("0 30 0 * * ?");
            try {
                Task task = new OptimizeLuceneIndexTask();

                Job job = _scheduler.addCustomTaskJob(JOBNAME_OPTIMIZE_LUCENE_INDEX, task, false,
                        optimizeIndexSchedule);
                job.setQuiet(true);
                job.setDescription("Optimizes the lucene index for size and performance");
                job.setOrigin(Job.ORIGIN_WGACONFIG);
                currentJobs.add(job.getName());
            } catch (Exception e) {
                getLog().error("Unable to add pending release publishing task to wga scheduler.", e);
            }
        } else {
            Job job = _scheduler.getJob(JOBNAME_OPTIMIZE_LUCENE_INDEX);
            if (job != null) {
                _scheduler.removeJob(JOBNAME_OPTIMIZE_LUCENE_INDEX);
            }
        }

    }

    private void adaptConfigurationToRegistry(boolean configMigrated) throws Exception, FileNotFoundException {

        boolean changed = false;

        // When migrated: remove default options from config (must be here so complete registry is available)
        if (configMigrated) {
            logCategoryInfo("Removing default options from migrated WGA configuration", 1);
            getWgaConfiguration().removeDefaultOptions(getModuleRegistry());
            changed = true;
        }

        // Create mandatory server options with full registry
        if (createMandatoryServerOptions()) {
            getLog().info("Creating mandatory server options in WGA configuration");
            changed = true;
        }

        if (changed == true) {
            getLog().info("Saving automatic adaptions to the WGA configuration");
            WGAConfiguration.write(getWgaConfiguration(), new FileOutputStream(configFile));
        }

    }

    private boolean createMandatoryServerOptions() {

        boolean somethingChanged = false;
        Map<String, String> serverOptions = getWgaConfiguration().getServerOptions();

        for (ModuleDefinition modDef : getModuleRegistry().getModulesForType(WGAServerOptionsModuleType.class)
                .values()) {
            for (OptionDefinition optDef : modDef.getOptionDefinitions().values()) {
                if (!optDef.isOptional() && optDef.getDefaultValue() != null) {
                    if (!serverOptions.containsKey(optDef.getName())) {
                        serverOptions.put(optDef.getName(), optDef.getDefaultValue());
                        somethingChanged = true;
                    }
                }
            }
        }

        return somethingChanged;

    }

    public static String retrieveConfigPath() throws IOException {
        String configFilePath = System.getProperty(WGACore.SYSPROPERTY_CONFIGPATH);
        if (WGUtils.isEmpty(configFilePath)) {
            String userHome = System.getProperty("user.home");
            File defaultConfigDir = new File(userHome, DEFAULT_CONFIG_DIR);
            if (!defaultConfigDir.exists()) {
                if (!defaultConfigDir.mkdirs()) {
                    throw new IOException(
                            "Unable to create default config directory " + defaultConfigDir.getPath());
                }
            }

            configFilePath = defaultConfigDir.getPath();
        }
        return configFilePath;
    }

    private void initAccessLogger() {

        // Close old access logger if present
        if (_accessLogger != null) {
            _accessLogger.close();
            _accessLogger = null;
        }

        if (_wgaConfiguration.getAccessLog() != null) {
            logCategoryInfo("Access logger", 1);
            try {
                _accessLogger = new WGALoggerWrapper(_wgaConfiguration.getAccessLog(), this);
            } catch (Exception e) {
                this.log.error("Exception instantiating access logger", e);
                getProblemRegistry()
                        .addProblem(Problem.create(new UpdateConfigOccasion(), AccessLoggingScope.INSTANCE,
                                "updateConfigProblem.accessLoggerException", ProblemSeverity.LOW, e));
                _accessLogger = null;
            }
        }

    }

    private synchronized void updateDatabaseServers() {

        logCategoryInfo("Database servers", 1);

        Map<String, WGDatabaseServer> newServers = new ConcurrentHashMap<String, WGDatabaseServer>();

        // Add singleton servers
        Iterator<ModuleDefinition> singletons = getModuleRegistry()
                .getModulesForType(DatabaseServerModuleType.class).values().iterator();
        while (singletons.hasNext()) {
            ModuleDefinition serverDefinition = singletons.next();
            DatabaseServerProperties properties = (DatabaseServerProperties) serverDefinition.getProperties();
            if (properties == null) {
                getLog().error("Database server type '" + serverDefinition.getTitle(Locale.getDefault()) + "' ("
                        + serverDefinition.getImplementationClass().getName()
                        + ") is invalid, misses neccessary properties definition");
            } else if (properties.isSingleton()) {
                try {
                    serverDefinition.testDependencies();
                    WGDatabaseServer server = (WGDatabaseServer) getModuleRegistry().instantiate(serverDefinition);
                    server.init(WGAConfiguration.SINGLETON_SERVER_PREFIX + server.getClass().getName(), null,
                            new HashMap<String, String>());
                    newServers.put(server.getUid(), server);
                    getLog().info("Registering database server '" + server.getTitle(Locale.getDefault())
                            + "' (Automatically created)");
                } catch (ModuleDependencyException e) {
                    getLog().error("Database server " + serverDefinition.getTitle(Locale.getDefault())
                            + " deactivated in current WGA runtime: " + e.getMessage());
                    getProblemRegistry().addProblem(Problem.create(new UpdateConfigOccasion(),
                            new DBServerScope(properties), "updateConfigProblem.serverMissingDependencies",
                            ProblemSeverity.HIGH, Problem.var("msg", e.getMessage()), e));
                } catch (Exception e) {
                    getLog().error("Exception instantiating database server", e);
                    getProblemRegistry()
                            .addProblem(Problem.create(new UpdateConfigOccasion(), new DBServerScope(properties),
                                    "updateConfigProblem.serverException", ProblemSeverity.HIGH, e));
                }
            }

        }

        // Add servers from configuration
        Iterator<DatabaseServer> servers = getWgaConfiguration().getDatabaseServers().iterator();
        while (servers.hasNext()) {
            DatabaseServer databaseServer = servers.next();
            if (databaseServer.isEnabled()) {
                try {
                    Class serverClass = getLibraryLoader().loadClass(databaseServer.getImplClassName());
                    ModuleDefinition serverDefinition = getModuleRegistry()
                            .getModuleDefinition(DatabaseServerModuleType.class, serverClass);
                    if (serverDefinition != null) {
                        serverDefinition.testDependencies();
                    }
                    WGDatabaseServer server = (WGDatabaseServer) getModuleRegistry().instantiate(serverClass);

                    Map<String, String> serverOptions = new HashMap<>();
                    putDefaultDbServerOptions(serverOptions);
                    serverOptions.putAll(databaseServer.getOptions());
                    server.init(databaseServer.getUid(), databaseServer.getTitle(), serverOptions);
                    newServers.put(server.getUid(), server);
                    getLog().info("Registering database server '" + server.getTitle(Locale.getDefault()) + "'");
                } catch (ModuleDependencyException e) {
                    getLog().error("Database server " + databaseServer.getTitle()
                            + " deactivated in current WGA runtime: " + e.getMessage());
                    getProblemRegistry().addProblem(Problem.create(new UpdateConfigOccasion(),
                            new DBServerScope(databaseServer), "updateConfigProblem.serverMissingDependencies",
                            ProblemSeverity.HIGH, Problem.var("msg", e.getMessage()), e));
                } catch (Exception e) {
                    getLog().error("Exception instantiating database server", e);
                    getProblemRegistry().addProblem(
                            Problem.create(new UpdateConfigOccasion(), new DBServerScope(databaseServer),
                                    "updateConfigProblem.serverException", ProblemSeverity.HIGH, e));
                }
            }
        }

        // Import state from previous servers
        for (WGDatabaseServer newServer : newServers.values()) {
            WGDatabaseServer oldServer = _databaseServers.get(newServer.getUid());
            if (oldServer != null) {
                newServer.importState(oldServer);
            }
        }

        _databaseServers = newServers;

    }

    private void putDefaultDbServerOptions(Map<String, String> serverOptions) {
        serverOptions.put(DatabaseServer.OPTION_SHAREDPOOL_LEGACY_DBCP_MONITORING,
                String.valueOf("true".equals(_wgaConfiguration.getServerOptions()
                        .get(WGAConfiguration.SERVEROPTION_SERVICE_INTEGRATEDJMX_LEGACY_DBCP))));
    }

    private Map<String, WGDatabaseServer> _databaseServers = new ConcurrentHashMap<String, WGDatabaseServer>();

    private List<HTMLHeadInclusion> _htmlHeadInclusions = new ArrayList<HTMLHeadInclusion>();

    private WGAPublisherOptionReader _publisherOptionsReader;

    private WGAServerOptionReader _serverOptionsReader;

    private void initModuleRegistry() {

        // Initialize module registry and retrieve definitions
        _moduleRegistry = new WGAModuleRegistry();
        WGFactory.setModuleRegistry(_moduleRegistry);
        _moduleRegistry.getContextObjects().put(WGACore.class, this);
        _moduleRegistry.getContextObjects().put(WGA.class, WGA.get(this));

        // Register mandatory registry change listeners
        _publisherOptionsReader = new WGAPublisherOptionReader();
        _moduleRegistry.addChangeListener(_publisherOptionsReader, ContentStorePublisherOptionsCollector.class);
        _moduleRegistry.addChangeListener(_publisherOptionsReader, ContentDatabasePublisherOptionsCollector.class);
        _moduleRegistry.addChangeListener(_publisherOptionsReader, ContentStorePublisherOptionsModuleType.class);
        _moduleRegistry.addChangeListener(_publisherOptionsReader, ContentDatabasePublisherOptionsModuleType.class);

        _serverOptionsReader = new WGAServerOptionReader();
        _moduleRegistry.addChangeListener(_serverOptionsReader, WGAServerOptionsModuleType.class);

        HTMLHeadInclusionUpdater inclusionUpdater = new HTMLHeadInclusionUpdater();
        _moduleRegistry.addChangeListener(inclusionUpdater, HTMLHeadInclusionModuleType.class);

        ServletFilterUpdater filterUpdater = new ServletFilterUpdater();
        _moduleRegistry.addChangeListener(filterUpdater, FilterConfigModuleType.class);
        _moduleRegistry.addChangeListener(filterUpdater, WGAWebServiceModuleType.class);

        FileAnnotatorUpdater fileAnnotatorUpdater = new FileAnnotatorUpdater();
        _moduleRegistry.addChangeListener(fileAnnotatorUpdater, FileAnnotatorModuleType.class);

        // Do initial module definition search
        try {
            _moduleRegistry.searchModuleDefinitions(this);
        } catch (IOException e) {
            getLog().error("Exception searching module definitions", e);
        }

        // Initialize other mandatory dependent objects
        PasswordOptionEncoder encoder;
        String passwordEncoderKey = _wgaConfiguration.getPasswordEncoding();
        try {
            ModuleDefinition passwordEncoderModDef = _moduleRegistry
                    .getModuleDefinitionByKey(PasswordEncodingType.class, passwordEncoderKey);
            if (passwordEncoderModDef != null) {
                Class passwordEncoderClass = passwordEncoderModDef.getImplementationClass();
                encoder = (PasswordOptionEncoder) passwordEncoderClass.newInstance();
            } else {
                getLog().error("Unknown password encoder key " + passwordEncoderKey);
                getLog().error("Falling back to Base64 encoder for encoding new passwords");
                encoder = new Base64();
            }
        } catch (Throwable e1) {
            getLog().error("Exception creating password encoder " + passwordEncoderKey, e1);
            getLog().error("Falling back to Base64 encoder for encoding new passwords");
            encoder = new Base64();
        }
        _moduleRegistry.getContextObjects().put(PasswordEncodingType.class, encoder);

    }

    private void logModuleRegistry() {

        // Log the most important registered modules to applog
        HashMap<ModuleDefinition, Throwable> dependencyFailures = new HashMap<ModuleDefinition, Throwable>();

        Iterator<ModuleDefinition> serverMods = _moduleRegistry.getModulesForType(DatabaseServerModuleType.class)
                .values().iterator();
        if (serverMods.hasNext()) {
            logCategoryInfo("Database server types available in this runtime", 2);
            while (serverMods.hasNext()) {
                ModuleDefinition moduleDefinition = serverMods.next();
                try {
                    moduleDefinition.testDependencies();
                    DatabaseServerProperties props = (DatabaseServerProperties) moduleDefinition.getProperties();
                    if (props != null && props.isSingleton()) {
                        getLog().info("- " + moduleDefinition.getTitle(Locale.ENGLISH) + " (Automatic instance)");
                    } else {
                        getLog().info("- " + moduleDefinition.getTitle(Locale.ENGLISH));
                    }
                } catch (Throwable e) {
                    dependencyFailures.put(moduleDefinition, e);
                }
            }
        }

        Iterator<ModuleDefinition> dbMods = _moduleRegistry.getModulesForType(ContentStoreModuleType.class).values()
                .iterator();
        if (dbMods.hasNext()) {
            logCategoryInfo("Content store types available in this runtime", 2);
            while (dbMods.hasNext()) {
                ModuleDefinition moduleDefinition = dbMods.next();
                try {
                    moduleDefinition.testDependencies();
                    getLog().info("- " + moduleDefinition.getTitle(Locale.ENGLISH));
                } catch (Throwable e) {
                    dependencyFailures.put(moduleDefinition, e);
                }
            }
        }

        Iterator<ModuleDefinition> customdbMods = _moduleRegistry.getModulesForType(ContentDatabaseModuleType.class)
                .values().iterator();
        if (customdbMods.hasNext()) {
            logCategoryInfo("Other content database types available in this runtime", 2);
            while (customdbMods.hasNext()) {
                ModuleDefinition moduleDefinition = customdbMods.next();
                try {
                    moduleDefinition.testDependencies();
                    getLog().info("- " + moduleDefinition.getTitle(Locale.ENGLISH));
                } catch (Throwable e) {
                    dependencyFailures.put(moduleDefinition, e);
                }
            }
        }

        Iterator<ModuleDefinition> persdbMods = _moduleRegistry
                .getModulesForType(PersonalisationDatabaseModuleType.class).values().iterator();
        if (persdbMods.hasNext()) {
            logCategoryInfo("Personalisation database types available in this runtime", 2);
            while (persdbMods.hasNext()) {
                ModuleDefinition moduleDefinition = persdbMods.next();
                try {
                    moduleDefinition.testDependencies();
                    getLog().info("- " + moduleDefinition.getTitle(Locale.ENGLISH));
                } catch (Throwable e) {
                    dependencyFailures.put(moduleDefinition, e);
                }
            }
        }

        Iterator<ModuleDefinition> authMods = _moduleRegistry
                .getModulesForType(AuthenticationSourceModuleType.class).values().iterator();
        if (authMods.hasNext()) {
            logCategoryInfo("Authentication types available in this runtime", 2);
            while (authMods.hasNext()) {
                ModuleDefinition moduleDefinition = authMods.next();
                try {
                    moduleDefinition.testDependencies();
                    getLog().info("- " + moduleDefinition.getTitle(Locale.ENGLISH));
                } catch (Throwable e) {
                    dependencyFailures.put(moduleDefinition, e);
                }
            }
        }

        Iterator<ModuleDefinition> wfMods = _moduleRegistry.getModulesForType(WorkflowEngineModuleType.class)
                .values().iterator();
        if (wfMods.hasNext()) {
            logCategoryInfo("Workflow engine types available in this runtime", 2);
            while (wfMods.hasNext()) {
                ModuleDefinition moduleDefinition = wfMods.next();
                try {
                    moduleDefinition.testDependencies();
                    getLog().info("- " + moduleDefinition.getTitle(Locale.ENGLISH));
                } catch (Throwable e) {
                    dependencyFailures.put(moduleDefinition, e);
                }
            }
        }

        Iterator<ModuleDefinition> designMods = _moduleRegistry.getModulesForType(DesignSourceModuleType.class)
                .values().iterator();
        if (designMods.hasNext()) {
            logCategoryInfo("Design source types available in this runtime", 2);
            while (designMods.hasNext()) {
                ModuleDefinition moduleDefinition = designMods.next();
                try {
                    moduleDefinition.testDependencies();
                    DesignSourceProperties props = (DesignSourceProperties) moduleDefinition.getProperties();
                    if (props != null && props.isSingleton()) {
                        getLog().info("- " + moduleDefinition.getTitle(Locale.ENGLISH) + " (Automatic instance)");
                    } else {
                        getLog().info("- " + moduleDefinition.getTitle(Locale.ENGLISH));
                    }
                } catch (Throwable e) {
                    dependencyFailures.put(moduleDefinition, e);
                }
            }
        }

        Iterator<ModuleDefinition> contentShareMods = _moduleRegistry.getModulesForType(ShareModuleType.class)
                .values().iterator();
        if (contentShareMods.hasNext()) {
            logCategoryInfo("Share types available in this runtime", 2);
            while (contentShareMods.hasNext()) {
                ModuleDefinition moduleDefinition = contentShareMods.next();
                try {
                    moduleDefinition.testDependencies();
                    getLog().info("- " + moduleDefinition.getTitle(Locale.ENGLISH));
                } catch (Throwable e) {
                    dependencyFailures.put(moduleDefinition, e);
                }
            }
        }

        Iterator<ModuleDefinition> taskMods = _moduleRegistry.getModulesForType(SchedulerTaskModuleType.class)
                .values().iterator();
        if (taskMods.hasNext()) {
            logCategoryInfo("Scheduler tasks available in this runtime", 2);
            while (taskMods.hasNext()) {
                ModuleDefinition moduleDefinition = taskMods.next();
                try {
                    moduleDefinition.testDependencies();
                    getLog().info("- " + moduleDefinition.getTitle(Locale.ENGLISH));
                } catch (Throwable e) {
                    dependencyFailures.put(moduleDefinition, e);
                }
            }
        }

        Iterator<ModuleDefinition> filterMods = _moduleRegistry.getModulesForType(FilterConfigModuleType.class)
                .values().iterator();
        if (filterMods.hasNext()) {
            logCategoryInfo("Request filters available in this runtime", 2);
            while (filterMods.hasNext()) {
                ModuleDefinition moduleDefinition = filterMods.next();
                try {
                    moduleDefinition.testDependencies();
                    getLog().info("- " + moduleDefinition.getTitle(Locale.ENGLISH));
                } catch (Throwable e) {
                    dependencyFailures.put(moduleDefinition, e);
                }
            }
        }

        Iterator<ModuleDefinition> clusterMods = _moduleRegistry.getModulesForType(ClusterServiceModuleType.class)
                .values().iterator();
        if (clusterMods.hasNext()) {
            logCategoryInfo("Cluster service implementations available in this runtime", 2);
            while (clusterMods.hasNext()) {
                ModuleDefinition moduleDefinition = clusterMods.next();
                try {
                    moduleDefinition.testDependencies();
                    getLog().info("- " + moduleDefinition.getTitle(Locale.ENGLISH));
                } catch (Throwable e) {
                    dependencyFailures.put(moduleDefinition, e);
                }
            }
        }

        Iterator<Map.Entry<ModuleDefinition, Throwable>> failureMods = dependencyFailures.entrySet().iterator();
        if (failureMods.hasNext()) {
            logCategoryInfo("Modules not available to this runtime because of missing dependencies", 2);
            while (failureMods.hasNext()) {
                Map.Entry<ModuleDefinition, Throwable> failure = failureMods.next();
                try {
                    getLog().info("- " + failure.getKey().getTitle(Locale.ENGLISH) + ": "
                            + failure.getValue().getMessage());
                } catch (Throwable e) {
                    getLog().error("- " + failure.getKey().getImplementationClass()
                            + ": Exception while logging dependency failure", e);
                }

            }
        }

    }

    private void createDefaultWGAConfiguration(File file) throws Exception {
        log.info("Creating default wga configuration in file '" + file.getAbsolutePath() + "'.");
        WGAConfiguration defaultConfig = WGAConfiguration.createDefaultConfig(WGAVersion.toCsConfigVersion());
        WGAConfiguration.write(defaultConfig, new FileOutputStream(file));
    }

    private void migrateWGAConfiguration(File oldConfigFile, File newConfigFile) throws Exception {
        MigrationResult migrationResult = WGAConfigurationMigrator.createFromWGAXML(
                new FileInputStream(oldConfigFile), oldConfigFile.getParentFile().getAbsolutePath());
        Iterator<MigrationMessage> logEntries = migrationResult.getLog().iterator();
        File migrationLogFile = new File(newConfigFile.getParentFile(), "migration.log");
        BufferedWriter writer = new BufferedWriter(new FileWriter(migrationLogFile));
        while (logEntries.hasNext()) {
            MigrationMessage message = logEntries.next();
            if (message.getLevel() == MigrationMessage.INFO) {
                log.info(message.getMessage());
                writer.write("INFO - " + message.getMessage() + "\n");
                writer.write("\n");
            } else if (message.getLevel() == MigrationMessage.WARNING) {
                if (message.getThrowable() != null) {
                    log.warn(message.getMessage(), message.getThrowable());
                    writer.write("WARN - " + message.getMessage() + "\n");
                    message.getThrowable().printStackTrace(new PrintWriter(writer));
                } else {
                    log.warn(message.getMessage());
                    writer.write("WARN - " + message.getMessage() + "\n");
                    writer.write("\n");
                }
            } else if (message.getLevel() == MigrationMessage.ERROR) {
                if (message.getThrowable() != null) {
                    log.error(message.getMessage(), message.getThrowable());
                    writer.write("ERROR - " + message.getMessage() + "\n");
                    message.getThrowable().printStackTrace(new PrintWriter(writer));
                    writer.write("\n");
                } else {
                    log.error(message.getMessage());
                    writer.write("ERROR - " + message.getMessage() + "\n");
                }
            }
        }
        writer.close();
        try {
            WGAConfiguration.write(migrationResult.getConfig(), new FileOutputStream(newConfigFile));
        } catch (Exception e) {
            newConfigFile.delete();
            throw e;
        }
    }

    private void initCustomCoreListeners() {

        // From configuration
        customCoreListeners = new ArrayList<Object>();
        Iterator classesIt = customCoreListenerClassnames.iterator();
        while (classesIt.hasNext()) {
            String clazzName = (String) classesIt.next();
            try {
                Class clazz = getLibraryLoader().loadClass(clazzName);
                Object listenerObj = clazz.newInstance();
                if (!(listenerObj instanceof WGACoreEventListener)) {
                    getLog().error("Event listener '" + clazzName + "' is no implementation of "
                            + WGACoreEventListener.class.getName());
                    continue;
                }

                addEventListener((WGACoreEventListener) listenerObj);
                customCoreListeners.add(listenerObj);
            } catch (ClassNotFoundException e) {
                getLog().error("Cannot load event listener '" + clazzName + "' because class could not be found");
            } catch (InstantiationException e) {
                getLog().error("Error instantiating event listener '" + clazzName + "'", e);
            } catch (IllegalAccessException e) {
                getLog().error("Access error instantiating event listener '" + clazzName + "'", e);
            }
        }

    }

    private void initTestCore() throws JAXBException {

        // if not set yet create TestCore and disable
        if (_testCore == null) {
            boolean testsEnabled = Boolean.parseBoolean(System.getProperty(SYSPROPERTY_UNITTEST));
            String logDir = System.getProperty(SYSPROPERTY_UNITTEST_LOGDIR, null);
            if (logDir != null) {
                _testCore = new TestCore(this, testsEnabled, logDir);
            } else {
                _testCore = new TestCore(this, testsEnabled);
            }
        }

    }

    private void initQuartz() {

        try {
            int threadCount = 5;
            String threadCountConfig = System.getProperty(SYSPROPERTY_QUARTZ_THREADCOUNT);
            if (threadCountConfig != null) {
                try {
                    threadCount = Integer.parseInt(threadCountConfig);
                } catch (NumberFormatException e) {
                    getLog().error("Unable to parse " + SYSPROPERTY_QUARTZ_THREADCOUNT
                            + " as integer. Using default of " + threadCount);
                }
            }

            int threadPriority = Thread.MIN_PRIORITY;
            String threadPrioConfig = System.getProperty(SYSPROPERTY_QUARTZ_THREADPRIORITY);
            if (threadPrioConfig != null) {
                try {
                    threadPriority = Integer.parseInt(threadPrioConfig);
                } catch (NumberFormatException e) {
                    getLog().error("Unable to parse " + SYSPROPERTY_QUARTZ_THREADPRIORITY
                            + " as integer. Using default of " + threadPriority);
                }
            }
            ThreadPool threadPool = new SimpleThreadPool(threadCount, threadPriority);
            threadPool.initialize();
            JobStore jobStore = new RAMJobStore();
            DirectSchedulerFactory.getInstance().createScheduler(threadPool, jobStore);
            _quartzScheduler = DirectSchedulerFactory.getInstance().getScheduler();
            _quartzScheduler.start();
        } catch (SchedulerException e) {
            getLog().fatal("Unable to start wga scheduler. Some background tasks may not run!", e);
        }

    }

    private Map<String, WGADomain> initReadDomains() {

        logCategoryInfo("Domains configuration", 1);

        // Map new configs
        Map<String, WGADomain> newDomainConfigs = new HashMap<String, WGADomain>();
        Iterator<Domain> domains = _wgaConfiguration.getDomains().iterator();
        while (domains.hasNext()) {
            Domain domain = domains.next();
            WGADomain domainConfig = new WGADomain(this, domain);

            // TODO this check should be done in configuration
            if (domainConfig.getName().startsWith(PluginConfig.PLUGIN_DBKEY_PREFIX)) {
                this.log.error("Domain name '" + domainConfig.getName() + "' invalid. The prefix '"
                        + PluginConfig.PLUGIN_DBKEY_PREFIX + "' is reserved for WGA plugin domains.");
                _problemRegistry.addProblem(
                        Problem.create(new UpdateConfigOccasion(), "updateConfigProblem.invalidDomainName",
                                ProblemSeverity.HIGH, Problem.var("name", domainConfig.getName())));
                continue;
            }

            newDomainConfigs.put(domainConfig.getName(), domainConfig);
            this.log.info("Reading configuration of domain '" + domainConfig.toString() + "'");

        }

        return newDomainConfigs;
    }

    private void initStartupDomains(Map<String, WGADomain> newDomainConfigs) {

        logCategoryInfo("Domains startup", 1);

        // Initialize new domain configurations (their auth modules, that is)
        Iterator<WGADomain> doms = newDomainConfigs.values().iterator();
        while (doms.hasNext()) {
            WGADomain domain = doms.next();
            logCategoryInfo("Domain " + domain.getName(), 2);
            try {
                domain.init();
            } catch (Exception e) {
                getLog().error("Exception initializing domain " + domain.getName(), e);
                getProblemRegistry()
                        .addProblem(Problem.create(new UpdateConfigOccasion(), new DomainScope(domain.getName()),
                                "updateConfigProblem.domainException", ProblemSeverity.HIGH, e));
            }
        }

        // Replace old domain configs with new domain configs
        Map<String, WGADomain> oldDomainConfigs = this.domains;
        this.domains = newDomainConfigs;
        closeDomainConfigs(oldDomainConfigs);
    }

    private void closeDomainConfigs(Map<String, WGADomain> oldDomainConfigs) {

        if (oldDomainConfigs == null) {
            return;
        }

        for (WGADomain cfg : oldDomainConfigs.values()) {
            try {
                cfg.destroy();
            } catch (Throwable e) {
                getLog().error("Exception closing domain " + cfg.getName(), e);
            }
        }

    }

    private void updateScheduler() {

        logCategoryInfo("Scheduler", 1);

        try {
            List<String> currentJobs = new ArrayList<String>();

            registerMandatoryJobs(currentJobs);

            try {
                String loggingDirStr = _wgaConfiguration.getSchedulerConfiguration().getLoggingDir();
                if (loggingDirStr != null) {
                    _scheduler.setLoggingDir(new File(loggingDirStr));
                } else {
                    _scheduler
                            .setLoggingDir(new File(getConfigFile().getParentFile(), Scheduler.LOGGINGDIR_DEFAULT));
                }
            } catch (IllegalArgumentException e) {
                getLog().error("Configuration error initializing directory for permanent job logs", e);
                getProblemRegistry().addProblem(Problem.create(new UpdateConfigOccasion(), GlobalScope.INSTANCE,
                        "updateConfigProblem.schedulerLoggingDirError", ProblemSeverity.HIGH, e));
            }

            // Register all currently configured jobs
            Iterator<de.innovationgate.wga.config.Job> jobs = _wgaConfiguration.getSchedulerConfiguration()
                    .getJobs().iterator();

            while (jobs.hasNext()) {
                de.innovationgate.wga.config.Job job = jobs.next();
                String jobName = job.getName();
                try {
                    _scheduler.addJob(job);
                    currentJobs.add(jobName);
                } catch (ConfigurationException e) {
                    getLog().error("Configuration error initializing job '" + jobName + "': " + e.getMessage(), e);
                    getProblemRegistry().addProblem(Problem.create(new UpdateConfigOccasion(),
                            new JobScope(job.getName()), "updateConfigProblem.jobConfigError", ProblemSeverity.LOW,
                            Problem.var("msg", e.getMessage()), e));
                } catch (Throwable e) {
                    getLog().error("Exception initializing job '" + jobName + "': " + e.getMessage(), e);
                    getProblemRegistry()
                            .addProblem(Problem.create(new UpdateConfigOccasion(), new JobScope(job.getName()),
                                    "updateConfigProblem.jobInitError", ProblemSeverity.LOW, e));
                }
            }

            // Remove all wgaconfig jobs, no more in configuration
            List jobsToRemove = new ArrayList(_scheduler.getJobNames());
            jobsToRemove.removeAll(currentJobs);
            Iterator removeIt = jobsToRemove.iterator();
            while (removeIt.hasNext()) {
                String jobName = (String) removeIt.next();
                Job job = _scheduler.getJob(jobName);
                if (job != null && job.getOrigin() == Job.ORIGIN_WGACONFIG) {
                    _scheduler.removeJob(jobName);
                }
            }
        } catch (RuntimeException e) {
            getLog().error("Failed to update WGA jobs", e);
            getProblemRegistry().addProblem(Problem.create(new UpdateConfigOccasion(),
                    "updateConfigProblem.jobException", ProblemSeverity.HIGH, e));
        }

    }

    private void parseConfigFile() throws Exception {
        try {
            logCategoryInfo("Loading WGA Configuration", 1);
            _wgaConfiguration = WGAConfiguration.read(new FileInputStream(configFile));
            List<ValidationError> errors = _wgaConfiguration.validate();
            if (!errors.isEmpty()) {
                log.error("WGA configuration contains validation errors:");
                for (ValidationError error : errors) {
                    log.error("- " + error.getMessage());
                }
            }
        } catch (ConfigValidationException e) {
            log.fatal("WGA configuration integrity check fails:");
            Iterator<ValidationError> errors = e.getValidationErrors().iterator();
            while (errors.hasNext()) {
                ValidationError error = errors.next();
                log.fatal("- " + error.getMessage());
            }
            throw e;
        }
    }

    /**
     * retrieves the old wga config file "wga.xml"
     * @return
     */
    private File retrieveOldConfigFile() {
        File configFile = null;
        String configFilePath = System.getProperty(WGACore.SYSPROPERTY_CONFIGPATH);

        if (configFilePath == null) {
            configFilePath = System.getProperty("user.home");
        }

        return new File(configFilePath, WGACore.OLD_CONFIGFILE_NAME);
    }

    /**
     * retrieve the new wga config file "wgaconfig.xml"
     * @return
     * @throws ServletException 
     */
    private File retrieveConfigFile() throws IOException {
        return new File(retrieveConfigPath(), WGACore.CONFIGFILE_NAME);
    }

    // Maps of objects
    private Map<String, WGDatabase> contentdbs = new ConcurrentHashMap<String, WGDatabase>();
    private Map<String, String> _contentdbUuidsToKeys = new ConcurrentHashMap<String, String>();

    Map<String, WGDatabase> personalisationdbs = new ConcurrentHashMap<String, WGDatabase>();

    private Map<String, MediaKey> systemMediaKeys = new ConcurrentHashMap<String, MediaKey>();
    private Map<String, MediaKey> customMediaKeys = new ConcurrentHashMap<String, MediaKey>();

    private Map<String, String> systemElements = new ConcurrentHashMap<String, String>();
    private Map<String, String> customElements = new ConcurrentHashMap<String, String>();

    private File configFile;

    private long configFileLastModified;

    private WGAPluginSet pluginSet;

    private TMLScriptGlobalRegistry _tmlscriptGlobalRegistry = new TMLScriptGlobalRegistry(this);

    private ScopeObjectRegistry _serverScopeObjectRegistry = new ScopeObjectRegistry(ObjectScope.SERVER, "Server",
            new NoopScopeObjectContextCreator());

    private int _status;

    protected WGAConfigurationOptionReader _variousServerOptionReader;

    public WGAConfigurationOptionReader getVariousServerOptionReader() {
        return _variousServerOptionReader;
    }

    private WGAFilter _filter;

    private String _errorPage;

    private AbstractWGAHttpSessionManager _httpSessionManager;

    private PageConnectionManager _pageConnectionManager = null;

    public PageConnectionManager getPageConnectionManager() {
        return _pageConnectionManager;
    }

    private IndependentWebSocketManager _independentWebSocketManager = null;

    public IndependentWebSocketManager getIndependentWebSocketManager() {
        return _independentWebSocketManager;
    }

    public static final String ATTRIB_IS_AJAX_SUBFORM_OF = "de.innovationgate.request.attrib.isAjaxSubFormOf";

    public static final String DBATTRIB_INCIDENTS_APP = "IncidentsApp";

    public static final String DEFAULT_FILE_ENCODING = "ISO-8859-1";

    public static final int DEFAULT_QUERY_MAXRESULTS = 500;

    public static final String ATTRIB_BI_VERSION4 = "BrowserInterfaceVersion4";

    public static final java.text.DateFormat DATEFORMAT_LOCAL = new java.text.SimpleDateFormat(
            "EEE, dd MMM yyyy HH:mm:ss z");

    public static final String SESSION_PORTLETMODES = "portletmodes";

    public static final String DBATTRIB_SESSIONCOOKIEDOMAIN = "SessionCookieDomain";

    public static final String ATTRIB_TMLDEBUG_TRACE_OPTIONS = "tmlDebugTraceOptions";

    public static final String DBATTRIB_CSCONFIG = "CSConfig";

    public static final String SESSION_FIREDPORTLETEVENTS = "firedPortletEvents";

    public static final String DBATTRIB_PLUGIN_FILETIME = "PluginFileTime";

    public static final String DBATTRIB_PLUGIN_VERSION = "PluginVersion";

    public static final Object WGASERVICES_WSDL_URL = "wgaservices.wsdl";

    public static final String ATTRIB_URLBUILDER = "URLBuilder";

    public static final String DBATTRIB_TITLEPATHURL_SHORTCUTAREA = "TitlePathURL.ShortcutArea";

    public static final String DBATTRIB_ALLOW_PUBLISHING = "AllowPublishing";

    public static final String DBATTRIB_SHARE_ALLOWALLCHARS = "Share.AllowAllCharacters";

    public static final String DBATTRIB_CREATE_NAME_URLS = "CreateNameURLs";

    public static final String ATTRIB_AUTHORINGMODE = "AuthoringMode.";

    public static final String DBATTRIB_PLUGIN_ID = "PluginID";

    /**
     * dummy dbkey for static tml requests
     */
    public static final String STATICTML_DESIGNDBKEY = "de.innovationgate.wga.statictml.DesignDB";

    public static final String DBATTRIB_HDBMODEL = "HDBModel";

    public static final String DBATTRIB_ENHANCED_ITEMEXPRESSIONS = "WebTML.EnhancedItemExpressions";

    private static final String ENCODER_NONE = "none";

    public static final String DBATTRIB_ENABLE_ACCESSLOGGING = "EnableAccessLogging";

    public static final String DBATTRIB_URLBUILDER = "URLBuilder";

    public static final String DBATTRIB_SECURE_APP = "SecureApp";

    public static final String DBATTRIB_LANGUAGEBEHAVIOUR_INSTANCE = "LanguageBehaviourInstance";

    public static final String DBATTRIB_EXTERNAL_FILE_SERVING_ENABLED = "ExternalFileServingEnabled";

    public static final String ATTRIB_VAR_PARAMETERS = "VarParameters";

    public static final String ATTRIB_TRANSIENTPORTLETREGISTRY = "TransientPortletRegistry";

    public static final String ATTRIB_PORTLETSTATESTORAGE = "PortletStateStorage";

    public static final String DBATTRIB_SERIALIZABLE_USERPROFILE = "SerializableUserProfile";

    public static final String DBATTRIB_AJAX_KEEP_URL_PARAMS = "AJAX.KeepURLParams";

    public static final String DBATTRIB_SHOW_SESSION_EXPIRED_MESSAGE = "AJAX.ShowSessionExpiredMessage";

    public static final String DBATTRIB_HOME_PAGE_NAME = "HomePageName";

    public static final String DBATTRIB_USE_LOGINPARAMETER_ON_CONTENTURLS = "UseLoginParameterOnContentURLs";

    public static final String DBATTRIB_FILE_DERIVATES_ENABLED = "FileDerivates.Enabled";

    public static final String DBATTRIB_FILE_DERIVATES_CREATORS = "FileDerivates.Creators";

    public static final String DBATTRIB_SCOPEOBJECTREGISTRY = "ScopeObjectRegistry";

    public static final String ATTRIB_URI_HASH = "URIHash";

    public static final String ATTRIB_FORMDATA = "FormData";

    public static final String ATTRIB_COOKIES = "HttpCookies";
    public static final String ATTRIB_AJAX_NOREFRESH = "AjaxNoRefresh";
    public static final String ATTRIB_AJAX_FAILURE = "AjaxFailure";
    public static final String ATTRIB_RESPONSE_SETUP = "ResponseSetup";
    public static final String ATTRIB_ACTIVE_PAGECONNECTIONS = "ActivePageConnections";
    public static final String ATTRIB_NEW_PAGE_CONNECTIONS = "NewPageConnections";

    public static final String ATTRIB_PAGECONNECTION = "PageConnection";

    public static final String ATTRIB_SESSION_LIFECYCLE_LISTENER = "SessionLifecycleListener";

    // Server options
    public WGPDispatcher getDispatcher() {
        return (WGPDispatcher) _context.getAttribute(ATTRIB_DISPATCHER);
    }

    public static Logger staticLog() {
        return log;
    }

    public Logger getLog() {
        return log;
    }

    /*
    public Document getConfigDocument() {
    return configDocument;
    }*/

    public Map<String, WGDatabase> getContentdbs() {
        return contentdbs;
    }

    public String getDefaultMediaKey() {
        return defaultMediaKey;
    }

    public WGPDeployer getDeployer() {
        return _deployer;
    }

    public Date getInstanceActiveSince() {
        return instanceActiveSince;
    }

    public Map<String, WGDatabase> getPersonalisationdbs() {
        return personalisationdbs;
    }

    public int getTmlBuffer() {
        return tmlBuffer;
    }

    public String getTmlHeader() {
        return tmlHeader;
    }

    public ObjectFormatter getEncodingFormatter(String encode, TMLContext context) throws FormattingException {

        encode = encode.toLowerCase();

        List<String> flagList = new ArrayList<String>();
        Set<String> flags = new HashSet<String>();
        // split up flags from encoder name
        int pos = encode.indexOf(":");
        if (pos != -1) {
            String strFlags = encode.substring(pos + 1);
            flagList = Arrays.asList(strFlags.split("\\|"));
            flags.addAll(flagList);
            encode = encode.substring(0, pos);
        }

        ObjectFormatter formatter = null;
        if (encode.equalsIgnoreCase(ENCODER_NONE)) {
            formatter = NoneEncodingFormatter.INSTANCE;
        } else if (encode.equalsIgnoreCase(ENCODER_HTML) || encode.equalsIgnoreCase(ENCODER_XML)) {
            formatter = new HTMLXMLEncodingFormatter(encode,
                    (context != null ? context.getDesignContext().getVersionCompliance()
                            : WGAVersion.toCsConfigVersion()));
        } else if (encode.equalsIgnoreCase(ENCODER_RTF)) {
            formatter = new RTFEncodingFormatter(flags);
        } else if (encode.equalsIgnoreCase(ENCODER_RTFSYSTEM)) {
            flags.add(RTFEncodingFormatter.FLAG_ONLY_SYSTEM_MACROS);
            formatter = new RTFEncodingFormatter(flags);
        } else if (encode.equalsIgnoreCase(ENCODER_CRLF)) {
            formatter = new CRLFEncoder();
        } else if (encode.equalsIgnoreCase(ENCODER_PLAINTEXT)) {
            formatter = new PlainTextFormatter();
        } else if (encode.equalsIgnoreCase(ENCODER_JAVASCRIPT)) {
            formatter = new JavaScriptEncodingFormatter();
        } else if (encode.equalsIgnoreCase(ENCODER_JSON)) {
            formatter = new JSONEncodingFormatter();
        } else if (encode.equalsIgnoreCase(ENCODER_NAMEPART)) {
            formatter = UniqueNamePartFormatter.INSTANCE;
        } else if (encode.equalsIgnoreCase(ENCODER_URL)) {
            formatter = new URLEncodingFormatter();
        } else if (encode.equalsIgnoreCase(ENCODER_URLQUERY)) {
            formatter = new URLQueryEncodingFormatter();
        } else {
            ModuleDefinition modDef = null;
            Class formatterClass = _systemFormatters.get(encode);
            if (formatterClass == null) {
                formatterClass = _customFormatters.get(encode);
            }

            if (formatterClass == null) {
                modDef = getModuleRegistry().getModuleDefinitionByKey(WebTMLEncoderModuleType.class, encode);
                if (modDef != null) {
                    try {
                        modDef.testDependencies();
                    } catch (ModuleDependencyException e) {
                        throw new FormattingException("WebTML encoder '" + encode
                                + "' not available bc. of missing dependency: " + e.getMessage());
                    }
                    formatterClass = modDef.getImplementationClass();
                }
            }

            if (formatterClass != null) {
                try {
                    if (modDef != null) {
                        formatter = (ObjectFormatter) getModuleRegistry().instantiate(modDef);
                    } else {
                        formatter = (ObjectFormatter) formatterClass.newInstance();
                    }
                } catch (Exception e) {
                    throw new FormattingException(
                            "The encoder class " + formatterClass.getName() + " is not instantiable", e);
                }
            } else {
                throw new FormattingException("Unknown encoder " + encode);
            }
        }

        if (formatter instanceof TMLContextAwareFormatter) {
            if (context != null) {
                ((TMLContextAwareFormatter) formatter).setContext(context);
            } else {
                throw new FormattingException("Encoder '" + encode + "' needs a WebTML context");
            }
        }

        if (formatter instanceof FlagAwareFormatter) {
            ((FlagAwareFormatter) formatter).setFlags(flagList);
        }

        return formatter;
    }

    public synchronized String tmlCacheDump() throws IOException {

        File outFile = new File(System.getProperty("user.home"),
                "tmlcachedump_" + new SimpleDateFormat("yyyyMMddHHmmSS").format(new Date()) + ".csv");
        FileWriter out = new FileWriter(outFile);
        getWebTMLCache().dump(out);
        out.flush();
        out.close();
        return outFile.getPath();

    }

    public File getLicenseFile() {
        return licenseFile;
    }

    public static WGACore retrieve(PageContext pageContext) {
        return (WGACore) pageContext.getAttribute(ATTRIB_CORE, PageContext.APPLICATION_SCOPE);
    }

    public static WGACore retrieve(ServletContext servletContext) {
        return (WGACore) servletContext.getAttribute(ATTRIB_CORE);
    }

    public boolean isTemporary() {
        return false;
    }

    public static ClassLoader getLibraryLoader() {
        return libraryClassLoadingChain;
    }

    public static List<Document> getDebugDocumentsList(HttpSession session) {

        synchronized (session) {
            @SuppressWarnings("unchecked")
            List<Document> debugDocuments = (List<Document>) session
                    .getAttribute(WGACore.ATTRIB_TMLDEBUG_DOCUMENTS);
            if (debugDocuments == null) {
                debugDocuments = new ArrayList<Document>();
                session.setAttribute(WGACore.ATTRIB_TMLDEBUG_DOCUMENTS, debugDocuments);
            }
            return debugDocuments;
        }

    }

    public boolean defaultActionSequenceIdAlreadyUsed(String sequenceId) {
        try {
            return (_calledSequenceIds.readEntry(sequenceId) != null);
        } catch (CacheException e) {
            getLog().error("Exception determining action sequence id usage", e);
            return false;
        }
    }

    public void defaultActionCalledWithSequenceId(String sequenceId) {
        try {
            _calledSequenceIds.writeEntry(sequenceId, sequenceId);
        } catch (CacheException e) {

        }
    }

    public synchronized void updateConfig() throws Exception {

        ProblemOccasion occ = new UpdateConfigOccasion();
        getProblemRegistry().clearProblemOccasion(occ);

        configFileLastModified = configFile.lastModified();
        WGAConfiguration oldConfig = getWgaConfiguration();

        parseConfigFile();

        // Update general config
        initReadGeneralConfig(true);

        // Update filter mappings
        initReadFilterMappings();
        if (getFilter() != null) {
            getFilter().initFilterChain();
        }

        // Event Manager
        _eventManager.reloadConfig();

        // Read domain configurations
        Map<String, WGADomain> newDomainConfigs = initReadDomains();

        deployErrorPage();

        // Update WGA plugin connections
        updatePlugins(newDomainConfigs);

        updateDatabaseServers();

        // Startup domain configurations (must be after plugin connection so that domains can use plugin-provided functionalities)
        initStartupDomains(newDomainConfigs);
        updateContentDBs();
        updateScheduler();
        updateShares();
        initAccessLogger();
        initExternalFileServing();
        initClusterService(oldConfig);
        initHttpSessionManager(oldConfig);
        _jmx.setup();

        cleanupProblemRegistry();

        WGAConfigurationUpdateEvent event = new WGAConfigurationUpdateEvent(this, getWgaConfiguration(), oldConfig);
        fireConfigEvent(event);

        logCategoryInfo("Reloading WGA Configuration finished", 1);

    }

    private void cleanupProblemRegistry() {

        for (ProblemScope scope : getProblemRegistry().getProblemScopes(AdministrativeProblemType.class)) {

            if (scope instanceof DatabaseScope) {
                String dbkey = ((DatabaseScope) scope).getDbkey();

                boolean stillExists = false;
                if (dbkey.startsWith(PluginConfig.PLUGIN_DBKEY_PREFIX)) {
                    // We have no good way to determine an existing plugin from its database key. So we will just keep everything.
                    stillExists = true;
                } else {
                    ContentDatabase dbConfig = getWgaConfiguration().getContentDatabase(dbkey);
                    if (dbConfig != null) {
                        stillExists = true;
                    }
                }
                if (!stillExists) {
                    getLog().info("Clearing problem registry scope for database '" + dbkey + "'");
                    getProblemRegistry().clearProblemScope(scope);
                }
            }

            else if (scope instanceof DomainScope) {
                String name = ((DomainScope) scope).getName();
                if (!name.equals("default")) {
                    Domain domain = getWgaConfiguration().getDomainByName(name);
                    if (domain == null) {
                        getLog().info("Clearing problem registry scope for domain '" + name + "'");
                        getProblemRegistry().clearProblemScope(scope);
                    }
                }
            }

            else if (scope instanceof DBServerScope) {
                String serverId = ((DBServerScope) scope).getServerId();
                DatabaseServer dbServer = (DatabaseServer) getWgaConfiguration().getByUid(serverId); // Configured servers
                if (dbServer == null) {

                    WGDatabaseServer server = getDatabaseServers().get(serverId); // Registered servers
                    if (server == null) {
                        getLog().info(
                                "Clearing problem registry scope for database server with id '" + serverId + "'");
                        getProblemRegistry().clearProblemScope(scope);
                    }
                }
            }

            else if (scope instanceof ContentShareScope) {
                String name = ((ContentShareScope) scope).getName();
                Share share = getWgaConfiguration().getShare(name);
                if (share == null) {
                    getLog().info("Clearing problem registry scope for content share '" + name + "'");
                    getProblemRegistry().clearProblemScope(scope);
                }
            }

            else if (scope instanceof JobScope) {
                String name = ((JobScope) scope).getName();
                de.innovationgate.wga.config.Job job = getWgaConfiguration().getSchedulerConfiguration()
                        .getJobByName(name);
                if (job == null) {
                    Job runtimeJob = getScheduler().getJob(name);
                    if (runtimeJob == null) {
                        getLog().info("Clearing problem registry scope for job '" + name + "'");
                        getProblemRegistry().clearProblemScope(scope);
                    }
                }
            }

            else if (scope instanceof PluginScope) {
                String name = ((PluginScope) scope).getPluginName();
                List<WGAPlugin> plugins = getPluginSet().getPluginsByUniqueName(name);
                if (plugins.size() == 0) {
                    getLog().info("Clearing problem registry scope for plugin '" + name + "'");
                    getProblemRegistry().clearProblemScope(scope);
                }
            }

        }

    }

    private void initExternalFileServing() {
        String enabled = getWgaConfiguration().getServerOptions()
                .get(WGAConfiguration.SERVEROPTION_EXTERNAL_FILE_SERVING_ENABLED);

        if (enabled != null) {
            _externalFileServingConfig.setEnabled(WGUtils.stringToBoolean(enabled));
        }

        if (getWgaConfiguration().getServerOptions()
                .get(WGAConfiguration.SERVEROPTION_EXTERNAL_FILE_SERVING_DIRECTORY) != null) {
            File dir = new File((String) getWgaConfiguration().getServerOptions()
                    .get(WGAConfiguration.SERVEROPTION_EXTERNAL_FILE_SERVING_DIRECTORY));
            _externalFileServingConfig.setDirectory(dir);
        } else if (_externalFileServingConfig.isEnabled()) {
            getLog().warn("Mandantory option '" + WGAConfiguration.SERVEROPTION_EXTERNAL_FILE_SERVING_DIRECTORY
                    + "' is missing. External file serving will be disabled.");
            _externalFileServingConfig.setEnabled(false);
        }

        if (getWgaConfiguration().getServerOptions()
                .get(WGAConfiguration.SERVEROPTION_EXTERNAL_FILE_SERVING_ROOT_URL) != null) {
            String url = (String) getWgaConfiguration().getServerOptions()
                    .get(WGAConfiguration.SERVEROPTION_EXTERNAL_FILE_SERVING_ROOT_URL);
            url = url.trim();
            if (!url.endsWith("/")) {
                url += "/";
            }
            _externalFileServingConfig.setRootURL(url);
        }

        if (getWgaConfiguration().getServerOptions()
                .get(WGAConfiguration.SERVEROPTION_EXTERNAL_FILE_SERVING_THRESHOLD) != null) {
            try {
                _externalFileServingConfig
                        .setThreshold(Long
                                .parseLong((String) getWgaConfiguration().getServerOptions()
                                        .get(WGAConfiguration.SERVEROPTION_EXTERNAL_FILE_SERVING_THRESHOLD))
                                * 1024);
            } catch (Exception e) {
                _externalFileServingConfig.setThreshold(ExternalFileServingConfig.DEFAULT_THRESHOLD);
                getLog().warn("Unable to parse external file serving threshold as long. Using default value "
                        + _externalFileServingConfig.getThreshold() / 1024 + "kb.", e);
            }
        } else {
            _externalFileServingConfig.setThreshold(ExternalFileServingConfig.DEFAULT_THRESHOLD);
        }

        if (_externalFileServingConfig.isEnabled()) {
            if (!_externalFileServingConfig.getDirectory().exists()) {
                _externalFileServingConfig.getDirectory().mkdirs();
            }

            if (!_externalFileServingConfig.getDirectory().exists()
                    || !_externalFileServingConfig.getDirectory().canWrite()) {
                getLog().warn("External file serving directory '"
                        + _externalFileServingConfig.getDirectory().getAbsolutePath()
                        + "' does not exist, could not be created or is not writable. External file serving will be disabled.");
                getProblemRegistry().addProblem(Problem.create(new UpdateConfigOccasion(),
                        "configUpdateProblem.extFileServingFailed", ProblemSeverity.HIGH,
                        Problem.var("dir", _externalFileServingConfig.getDirectory().getAbsolutePath())));
                _externalFileServingConfig.setEnabled(false);
            }
        }
    }

    private void deployErrorPage() {
        // deploy error page
        if (_wgaConfiguration.isCustomErrorPageEnabled() && _wgaConfiguration.getCustomErrorPage() != null) {
            try {
                this.log.info("Deploying custom error page.");
                _errorPage = getDeployer().deployErrorPage(_wgaConfiguration.getCustomErrorPage());
            } catch (IOException e) {
                this.log.error("Error deploying error page.", e);
                _problemRegistry.addProblem(Problem.create(new UpdateConfigOccasion(),
                        "updateConfigProblem.errorPageDeploymentError", ProblemSeverity.HIGH));
            }
        }
    }

    private void unDeployErrorPage() {
        if (_errorPage != null) {
            String erroPagePath = getServletContext().getRealPath(_errorPage);
            File errorPage = new File(erroPagePath);
            if (errorPage.exists()) {
                errorPage.delete();
            }
        }
    }

    private synchronized void updatePlugins(Map<String, WGADomain> domainConfigs) {

        logCategoryInfo("Plugins", 1);

        Set<String> newConnectedDBKeys = new HashSet<String>();

        if (_wgaDataDir == null) {
            getLog().warn("Unable to handle WGA plugins bc. the WGA data dir could not be initialized!");
            return;
        }

        try {
            WGFactory.getInstance().closeSessions();

            // Create plugins base dir
            File pluginsDir = new File(_wgaDataDir, "plugins");
            if (!pluginsDir.exists() && !pluginsDir.mkdir()) {
                getLog().error("Could not create plugins directory '" + pluginsDir.getPath()
                        + "'. No WGA Plugins will be connected.");
                _problemRegistry.addProblem(
                        Problem.create(new UpdateConfigOccasion(), "updateConfigProblem.invalidPluginsDir",
                                ProblemSeverity.HIGH, Problem.var("dir", pluginsDir.getAbsolutePath())));
                return;
            }

            // Load plugins definition file
            File pluginsDefFile = new File(pluginsDir, "plugins.xml");
            WGAPluginSet currentPlugins;
            boolean reinstallPluginsDir = false;
            if (pluginsDefFile.exists()) {
                currentPlugins = WGAPluginSet.load(pluginsDefFile);
            } else {
                currentPlugins = new WGAPluginSet();
                reinstallPluginsDir = true;
            }
            currentPlugins.init(this, pluginsDir);
            if (this.pluginSet != null) {
                currentPlugins.importRuntimeContexts(this.pluginSet);
            }

            // Replace plugins field with new plugin set - This must be done before connecting plugins so plugin init/connection scripts can
            // refer to already installed plugins
            this.pluginSet = currentPlugins;

            // Cope with default plugins. Install new, uninstall removed (must be in that order so installation can determine status of previous version to remove)
            // At last ensure that for every default plugin of the distro at least one version is active
            installDefaultPlugins(currentPlugins, reinstallPluginsDir);
            uninstallRemovedDefaultPlugins();
            ensureActiveDefaultPlugins();

            // Validate and connect plugins
            currentPlugins.validatePlugins();
            newConnectedDBKeys = currentPlugins.connectPlugins(domainConfigs);

            // Save plugins def file
            this.pluginSet.save();

            // Update module registry if any plugins got connected
            if (newConnectedDBKeys.size() > 0) {
                List<URLClassLoader> loaders = new ArrayList<URLClassLoader>();
                for (String dbkey : newConnectedDBKeys) {
                    DynamicClassLoadingChain.SubLoader loader = getLibraryClassLoadingChain().getSubLoader(dbkey);
                    if (loader != null) {
                        loaders.add(loader);
                    }
                }

                try {
                    _moduleRegistry.searchModuleDefinitions(this, loaders);
                } catch (IOException e) {
                    getLog().error("Exception searching module definitions", e);
                }
            }

        } catch (Throwable e) {
            getLog().error("Error initializing plugins", e);
            Problem.create(new UpdateConfigOccasion(), "updateConfigProblem.pluginUpdateFailed",
                    ProblemSeverity.HIGH);
        }

        return;

    }

    private void ensureActiveDefaultPlugins() throws MalformedURLException {

        Iterator defPlugins = getServletContext().getResourcePaths("/WEB-INF/default-plugins/").iterator();
        File managementPluginFile = null;
        while (defPlugins.hasNext()) {
            String pluginPath = (String) defPlugins.next();
            URL pluginURL = getServletContext().getResource(pluginPath);
            if (pluginURL == null) {
                continue;
            }

            String fileName = pluginURL.getFile();
            if (!fileName.endsWith(PluginConfig.WGAPLUGIN_SUFFIX)) {
                continue;
            }

            File pluginFile = new File(getServletContext().getRealPath(pluginPath));

            try {
                WGAPlugin.Configuration config = WGAPlugin.loadConfiguration(pluginFile, false);
                if (config != null) {
                    WGAPlugin activePlugin = this.pluginSet.getActivePluginsByUniqueName()
                            .get(config.getCsConfig().getPluginConfig().getId().getUniqueName());
                    if (activePlugin == null) {
                        WGAPlugin distroPlugin = this.pluginSet.getPluginByFile(pluginFile);
                        if (distroPlugin != null && distroPlugin.isActive() == false) {
                            this.pluginSet.activatePlugin(distroPlugin,
                                    WGAPluginSet.UPDATESTRATEGY_UPDATE_KEEP_DATA, false, null);
                        } else {
                            this.pluginSet.installPlugin(pluginFile, WGAPluginSet.UPDATESTRATEGY_UPDATE_KEEP_DATA,
                                    true);
                        }

                    }
                }
            } catch (Exception e) {
                getLog().error("Exception ensuring active default plugin  " + pluginFile.getName(), e);
            }

        }

    }

    protected boolean uninstallRemovedDefaultPlugins() {

        boolean anythingRemoved = false;
        for (WGAPlugin plugin : getPluginSet().getPlugins()) {
            if (plugin.isDefaultPlugin() || plugin.getRegisteredFilePath().contains("${wga.devpluginsdir}")) {

                Configuration pluginConfig = null;
                try {
                    pluginConfig = WGAPlugin.loadConfiguration(plugin.getPluginFile(), false);
                } catch (Exception e) {
                    getLog().error("Exception reading configuration of default plugin "
                            + plugin.getPluginFile().getAbsolutePath(), e);
                }

                if (pluginConfig == null) {
                    if (plugin.isActive()) {
                        try {
                            getPluginSet().deactivatePlugin(plugin);
                            disconnectPlugin(plugin);
                        } catch (Exception e) {
                            getLog().error("Exception disabling removed default plugin", e);
                        }
                    }
                    getPluginSet().uninstallPlugin(plugin, false);
                    anythingRemoved = true;
                }
            }
        }

        if (anythingRemoved) {
            try {
                getPluginSet().save();
            } catch (Exception e) {
                getLog().error("Exception uninstalling removed default plugins", e);
            }
        }

        return anythingRemoved;

    }

    public synchronized void updatePlugins() {
        updatePlugins(this.domains);
        updateContentDBs(); // Bc. of design consumers that may have been disconnected
        cleanupProblemRegistry();
    }

    private boolean installDefaultPlugins(WGAPluginSet currentPlugins, boolean reinstallPluginsFolder)
            throws FileSystemException, MalformedURLException {

        boolean anythingInstalled = false;

        // Reinstall the plugins folder if told to
        if (reinstallPluginsFolder) {
            File pluginsFolder = currentPlugins.getPluginFilesDir();
            File[] files = pluginsFolder.listFiles();
            for (int i = 0; i < files.length; i++) {
                File file = files[i];
                if (!file.getName().endsWith(PluginConfig.WGAPLUGIN_SUFFIX)) {
                    continue;
                }

                try {
                    WGAPlugin.Configuration config = WGAPlugin.loadConfiguration(file, false);
                    currentPlugins.installPlugin(file, WGAPluginSet.UPDATESTRATEGY_UPDATE_KEEP_DATA, false);
                    anythingInstalled = true;

                } catch (Exception e) {
                    getLog().error("Exception installing plugin file " + file.getName(), e);
                }
            }
        }

        // STEP 1: Collect all default plugins to priorize identical plugins from different locations
        Map<PluginID, File> defaultPlugins = new LinkedHashMap<PluginID, File>();

        // Default plugins from within WGA distribution
        Iterator defPlugins = getServletContext().getResourcePaths("/WEB-INF/default-plugins/").iterator();
        File managementPluginFile = null;
        while (defPlugins.hasNext()) {
            String pluginPath = (String) defPlugins.next();
            URL pluginURL = getServletContext().getResource(pluginPath);
            if (pluginURL == null) {
                continue;
            }

            String fileName = pluginURL.getFile();
            if (!fileName.endsWith(PluginConfig.WGAPLUGIN_SUFFIX)) {
                continue;
            }

            File pluginFile = new File(getServletContext().getRealPath(pluginPath));
            try {
                WGAPlugin.Configuration config = WGAPlugin.loadConfiguration(pluginFile, false);
                if (config != null) {
                    defaultPlugins.put(config.getCsConfig().getPluginConfig().getId(), pluginFile);
                }
            } catch (Exception e) {
                getLog().error("Exception installing registering plugin file " + pluginFile.getName()
                        + " for installation", e);
            }

        }

        // Additionally custom folder for default plugins
        String customDefaultPlugins = System.getProperty(SYSPROPERTY_DEFAULT_PLUGINS);
        if (customDefaultPlugins != null) {
            File pluginsFolder = getWGAFile(customDefaultPlugins);
            if (pluginsFolder != null && pluginsFolder.isDirectory()) {
                File[] files = pluginsFolder.listFiles();
                for (int i = 0; i < files.length; i++) {
                    File file = files[i];

                    if (!file.getName().endsWith(PluginConfig.WGAPLUGIN_SUFFIX)) {
                        continue;
                    }

                    try {
                        WGAPlugin.Configuration config = WGAPlugin.loadConfiguration(file, false);
                        if (config != null) {
                            defaultPlugins.put(config.getCsConfig().getPluginConfig().getId(), file);
                        }
                    } catch (Exception e) {
                        getLog().error("Exception installing registering plugin file " + file.getName()
                                + " for installation", e);
                    }
                }
            } else {
                getLog().error("The default plugins folder '" + customDefaultPlugins
                        + "' does not exist or is no directory");
            }
        }

        // Folder with "developer plugins", i.e. design directories containing plugin code
        String devPlugins = getDeveloperPluginsPath();
        if (devPlugins != null) {
            File pluginsFolder = getWGAFile(devPlugins);
            if (pluginsFolder != null && pluginsFolder.isDirectory()) {
                File[] files = pluginsFolder.listFiles();
                for (int i = 0; i < files.length; i++) {
                    File file = files[i];
                    if (!file.isDirectory()) {
                        if (!file.getName().endsWith(PluginConfig.WGAPLUGIN_SUFFIX)) {
                            continue;
                        }
                    }

                    try {
                        WGAPlugin.Configuration config = WGAPlugin.loadConfiguration(file, false);
                        if (config != null) {
                            defaultPlugins.put(config.getCsConfig().getPluginConfig().getId(), file);
                        }

                    } catch (Exception e) {
                        getLog().error("Exception installing registering plugin file " + file.getName()
                                + " for installation", e);
                    }

                }
            } else {
                getLog().error("The default plugins folder '" + devPlugins + "' does not exist or is no directory");
            }
        }

        // STEP 2: Actually install collected default plugins - only one plugin per uname/version
        for (File file : defaultPlugins.values()) {
            if (installDefaultPlugin(currentPlugins, file)) {
                anythingInstalled = true;
            }
        }

        return anythingInstalled;
    }

    private boolean installDefaultPlugin(WGAPluginSet currentPlugins, File file) {
        try {
            WGAPlugin.Configuration config = WGAPlugin.loadConfiguration(file, false);
            if (config == null) {
                return false;
            }

            // Look if the plugin is already installed
            PluginID defaultPluginId = config.getCsConfig().getPluginConfig().getId();
            List<WGAPlugin> installedPlugins = currentPlugins
                    .getPluginsByUniqueName(defaultPluginId.getUniqueName());
            for (WGAPlugin plugin : installedPlugins) {

                // If the plugin file does not exist (removed, not yet uninstalled, default plugin), there's no point in testing
                if (!plugin.getPluginFile().exists()
                        || WGAPlugin.loadConfiguration(plugin.getPluginFile(), false) == null) {
                    continue;
                }

                // Same plugin file = plugin already installed
                if (plugin.getPluginFile().equals(file)) {
                    return false;
                }

                // Don't overwrite (active) dev plugin dirs
                if (plugin.isDirectory() && plugin.isActive()) {
                    return false;
                }

                // Skip reasons that do not apply to dev plugins
                if (!file.isDirectory()) {

                    // Don't overwrite (active) plugins that are of higher version
                    int versionComparison = plugin.getPluginID().getVersion()
                            .compareTo(defaultPluginId.getVersion());
                    if (versionComparison > 0 && plugin.isActive()) {
                        return false;
                    }

                    // Don't overwrite (active) plugins of same version and same or higher build
                    if (versionComparison == 0 && plugin.isActive() && plugin.getPluginID().getVersion()
                            .getBuildVersion() >= defaultPluginId.getVersion().getBuildVersion()) {
                        return false;
                    }
                }
            }

            currentPlugins.installPlugin(file, WGAPluginSet.UPDATESTRATEGY_UPDATE_KEEP_DATA, true);
            return true;

        } catch (Exception e) {
            getLog().error("Exception installing default plugin file " + file.getName(), e);
            return false;
        }

    }

    /**
     * This method creates a file path to the given file that, if possible, is relative to the current WGA runtime folders.
     * If the file is below any WGA configation paths the path returned will be relative to it and so independent of the
     * absolute location of the WGA configuration. The WGA configuration folders are then represented as path variables.
     * If the file is not below any WGA Configuration path the absolute file path is returned. 
     * You must use method {@link #getWGAFile(String)} to resolve the paths created by this method and retrieve the file again
     * @param file
     * @return
     */
    public String createWGAFilePath(File file) {

        try {
            String devPluginsPath = getDeveloperPluginsPath();
            if (devPluginsPath != null) {
                String path = WGUtils.relativeFilePath(file.getAbsolutePath(), devPluginsPath);
                return "${wga.devpluginsdir}/" + path;
            }
        } catch (IllegalArgumentException e) {
        }

        try {
            String path = WGUtils.relativeFilePath(file.getAbsolutePath(), getWgaDataDir().getAbsolutePath());
            return "${wga.datadir}/" + path;
        } catch (IllegalArgumentException e) {
        }

        try {
            String path = WGUtils.relativeFilePath(file.getAbsolutePath(), getConfigFilePath());
            return "${wga.cfgdir}/" + path;
        } catch (IllegalArgumentException e) {
        }

        try {
            String path = WGUtils.relativeFilePath(file.getAbsolutePath(),
                    getServletContext().getRealPath("/WEB-INF/default-plugins"));
            return "${wga.defaultpluginsdir}/" + path;
        } catch (IllegalArgumentException e) {
        }

        return file.getAbsolutePath();

    }

    public WGDatabase connectPlugin(WGAPlugin plugin, Map domainConfigs, Set connectedPlugins)
            throws Problem, InvalidPluginException, WGIllegalArgumentException, FileSystemException, IOException {

        // Look if already connected to the correct file
        String dbKey = plugin.buildDatabaseKey();
        WGDatabase db = contentdbs.get(dbKey);
        if (db != null) {
            try {
                Long pluginFileTime = (Long) db.getAttribute(DBATTRIB_PLUGIN_FILETIME);
                Version pluginVersion = (Version) db.getAttribute(DBATTRIB_PLUGIN_VERSION);
                if (pluginVersion.equals(plugin.getPluginID().getVersion()) && (pluginFileTime != null
                        && pluginFileTime.equals(new Long(plugin.getFileLastModified())))) {
                    if (!db.isSessionOpen()) {
                        db.openSession();
                    }
                    return null;
                } else {
                    removeContentDB(dbKey);
                    db = null;
                }
            } catch (Exception e) {
                throw new InvalidPluginException(plugin, "Error checking existent plugin database " + dbKey, e);
            }
        }

        if (!plugin.isActive()) {
            throw new InvalidPluginException(plugin, "Plugin is deactivated");
        }
        if (!plugin.isValid()) {
            throw new InvalidPluginException(plugin, "Plugin is invalid");
        }

        // First connect all mandatory plugins
        try {

            Iterator mandatoryPlugins = plugin.getMandatoryPlugins().values().iterator();
            while (mandatoryPlugins.hasNext()) {
                WGAPlugin mandatoryPlugin = (WGAPlugin) mandatoryPlugins.next();
                connectPlugin(mandatoryPlugin, domainConfigs, connectedPlugins);
            }
        }
        // A mandatory plugin is invalid. Cancel connect.
        catch (InvalidPluginException e) {
            throw e;
        }

        logCategoryInfo("Plugin " + plugin.getInstallationKey(), 2);

        // Mandatory db options (for plugins)
        Map<String, String> dbOptions = new HashMap<String, String>();
        dbOptions.put(WGDatabase.COPTION_DBREFERENCE, dbKey.toLowerCase());
        dbOptions.put(WGDatabase.COPTION_READERPROFILECREATION, "true");
        putDefaultDbOptions(dbOptions);
        dbOptions.put(WGDatabase.COPTION_CLUSTERED, "false");

        // We try to automatically migrate plugin content stores to CS5 format
        dbOptions.put(WGDatabase.COPTION_CONTENT_STORE_VERSION, String.valueOf(WGDatabase.CSVERSION_WGA5));
        dbOptions.put(Database.OPTION_PATH, plugin.getInstallationKey());

        // Ugly hack to insert DB options for plugin debugging
        File optionsFile = getWGAFile(plugin.getPluginID().getUniqueName() + ".dboptions.properties");
        if (optionsFile != null && optionsFile.exists()) {
            Properties props = new Properties();
            FileInputStream in = new FileInputStream(optionsFile);
            props.load(in);
            in.close();
            for (Map.Entry<Object, Object> option : props.entrySet()) {
                dbOptions.put(String.valueOf(option.getKey()), String.valueOf(option.getValue()));
            }
        }

        // Clear the plugin database before connecting if the plugin is updated and should clear the db on update
        if (plugin.getRuntimeContext().isUpdated()) {
            if (plugin.getCsConfig()
                    .getPluginConfig() instanceof de.innovationgate.wga.common.beans.csconfig.v3.PluginConfig) {
                de.innovationgate.wga.common.beans.csconfig.v3.PluginConfig v3Config = (de.innovationgate.wga.common.beans.csconfig.v3.PluginConfig) plugin
                        .getCsConfig().getPluginConfig();
                if (v3Config.isClearDatabaseOnUpdate()) {
                    getLog().info("Clearing plugin database for installation key " + plugin.getInstallationKey());
                    plugin.getParent().deletePluginDatabase(plugin);
                }
            }
            plugin.getRuntimeContext().setUpdated(false);
        }

        // Optionally add hotpatches path
        String hotPatchesPath = System.getProperty(SYSPROPERTY_JDBC_HOTPATCHES);
        if (hotPatchesPath != null) {
            File hotPatchesFile = getWGAFile(hotPatchesPath);
            if (hotPatchesFile != null) {
                dbOptions.put(WGDatabaseImpl.COPTION_HOTPATCH, hotPatchesFile.getAbsolutePath());
            }
        }

        // Determine WGAPI implementation
        boolean usesDatabase = true;
        if (plugin.getCsConfig()
                .getPluginConfig() instanceof de.innovationgate.wga.common.beans.csconfig.v5.PluginConfig) {
            de.innovationgate.wga.common.beans.csconfig.v5.PluginConfig v5PluginConfig = (de.innovationgate.wga.common.beans.csconfig.v5.PluginConfig) plugin
                    .getCsConfig().getPluginConfig();
            if ("true".equals(v5PluginConfig.getOptions()
                    .get(de.innovationgate.wga.common.beans.csconfig.v5.PluginConfig.OPTION_NO_DATABASE))) {
                usesDatabase = false;
            }
        }

        Class<? extends WGDatabaseCore> implClass;
        if (usesDatabase) {
            if (!"false".equals(System.getProperty("de.innovationgate.wga.plugin.lazydbs"))) {
                implClass = de.innovationgate.webgate.api.hsql.WGLazyDatabaseImpl.class;
            } else {
                implClass = de.innovationgate.webgate.api.hsql.WGDatabaseImpl.class;
            }
        } else {
            implClass = WGFakeContentStore.class;
        }

        // Connect
        getLog().info("Connecting plugin " + plugin.getPluginID().getUniqueName() + " Version "
                + plugin.getPluginID().getVersion().toString());

        try {
            db = getPluginSet().getDbServer().openDatabase(implClass, dbOptions);
        } catch (Throwable e1) {
            throw new InvalidPluginException(plugin,
                    "Could not connect plugin \"" + plugin.getPluginID().getUniqueName() + "\"", e1);
        }

        if (db == null || !db.isSessionOpen()) {
            throw new InvalidPluginException(plugin, "Could not connect plugin \""
                    + plugin.getPluginID().getUniqueName() + "\" - Check logged messages above for error details");
        }

        try {
            db.getSessionContext().setTask("Initializing database in WGA");

            // Plugin dbs are always CS5 since they are automatically migrated
            //getLog().info("Database of plugin " + plugin.getPluginID().getUniqueName() + " is content store version " + db.getContentStoreVersion());

            PluginConfig pc = plugin.getCsConfig().getPluginConfig();
            String auth = pc.getAuthentication();
            db.setTitle(pc.getTitle());

            // Set mandatory database attributes
            initializeDBAttributes(db, dbKey, dbKey, new HashSet());

            // Create authentication
            if (auth != null) {
                String authImplClass = null;
                Map<String, String> authOptions = new HashMap<String, String>();

                // Delegate authentication to the default domain
                if (auth.equals(PluginConfig.AUTHSOURCE_DEFAULT_DOMAIN)) {
                    authImplClass = WGAAuthModuleFactory.AUTHMODULE_DELEGATE;
                    authOptions.put(DelegatingAuthModule.COPTION_DOMAIN, "default");
                }
                // Use some plugin for authentication
                else {
                    WGAPlugin authPlugin = plugin.getParent().getPluginByUniqueName(auth);
                    if (authPlugin != null) {
                        authImplClass = CSAuthModule.class.getName();
                        authOptions.put(CSAuthModule.COPTION_DBKEY, authPlugin.buildDatabaseKey());
                    } else {
                        getLog().error("Unable to find authentication plugin " + auth);
                    }
                }

                if (authImplClass != null) {
                    AuthenticationModule authModule = WGFactory.getAuthModuleFactory().getAuthModule(authImplClass,
                            authOptions, db);
                    db.setAuthenticationModule(authModule);
                }

            }

            // Enforce some plugin settings via db attributes
            if (usesDatabase) {
                db.setAttribute(DBATTRIB_PERSMODE, String.valueOf(pc.getPersonalisationMode()));
            } else {
                db.setAttribute(DBATTRIB_PERSMODE, String.valueOf(Constants.PERSMODE_SESSION));
            }
            db.setAttribute(DBATTRIB_PERSSTATMODE, String.valueOf(Constants.PERSSTATMODE_SESSION));
            db.setAttribute(DBATTRIB_PLUGIN_FILETIME, new Long(plugin.getFileLastModified()));
            db.setAttribute(DBATTRIB_PLUGIN_ID, plugin.getPluginID());
            db.setAttribute(DBATTRIB_PLUGIN_VERSION, plugin.getPluginID().getVersion());

            if (!pc.isUsageAsContentStore()) {
                db.setAttribute(DBATTRIB_ALLOW_PUBLISHING, "false");
            }

            if (!pc.isShowOnStartPage()) {
                db.setAttribute(DBATTRIB_STARTPAGE, "false");
            }

            // Configure design provider
            DesignReference ref = new DesignReference(Constants.DESIGNCOL_PLUGIN, plugin.getInstallationKey(),
                    null);
            Map<String, String> options = new HashMap<String, String>();
            db.setDesignProvider(
                    new FileSystemDesignProvider(ref, this, db, plugin.getDesignURL().toString(), options));
            db.setAllowDesignModification(false);
            getDesignFileCache().flushGroup(ref.toString());

            // Determine if ACL is empty
            boolean aclEmpty = false;
            try {
                if (db.isConnected() && db.hasFeature(WGDatabase.FEATURE_ACL_MANAGEABLE)
                        && db.getACL().getAllEntries().size() == 0) {
                    aclEmpty = true;
                }
            } catch (WGBackendException e1) {
                getLog().error("Error retrieving ACL state of db '" + db.getDbReference() + "'", e1);
            }

            // Process system container
            SystemContainerManager.SystemContainerContext scContext = null;
            try {
                scContext = _systemContainerManager.addDatabase(db, plugin, aclEmpty);
            } catch (Problem p) {
                throw p;
            } catch (Exception e) {
                throw new InvalidPluginException(plugin, "Exception processing system file container for plugin '"
                        + plugin.getPluginID().getUniqueName() + "'", e);
            }

            // Build map of publisher options from wga.xml. We only use gobal options here since plugins have no own options in wga.xml
            // and csconfig.xml options are processed via system container
            Map<String, String> publisherOptions = new HashMap<String, String>();
            // publisherOptions.putAll(_globalPublisherOptions); Plugins should not be influenced by global options of the current configuration
            if (scContext != null) {
                scContext.putPublisherOptions(publisherOptions);
            }

            // Publisher options initialisation which is equal for content dbs and plugins
            processPublisherOptions(db, publisherOptions);

            // Set plugin homepage. The method chooses either the plugin-specific homepage or the publisher option
            // Must be after publisher option initialisation to be able to react on them
            db.setAttribute(DBATTRIB_HOME_PAGE, plugin.getPluginHomepage());

            // check if db is empty before hdb script runs
            boolean isEmptyDB = db.isContentEmpty();

            // Validate default language definition
            if (!isEmptyDB) {
                db.determineDefaultLanguage();
            }

            // Add listeners for events and register embedded event scripts
            db.addDatabaseEventListener(_eventManager);
            db.addContentEventListener(_eventManager);
            db.addWorkflowEventListener(_eventManager);
            if (db.isConnected()) {
                _eventManager.updateDatabaseEvents(db);
            }

            // Add file annotators
            updateFileAnnotators(db);

            // add File Converter
            db.setFileConverter(_fileConverter);

            // System container initialisations
            if (scContext != null) {
                scContext.performInitialisation(new Boolean(isEmptyDB));
                if (isEmptyDB) {
                    db.onConnect(new ValidateDefaultLanguageAction());
                }
            }

            // Registering connection
            this.contentdbs.put(db.getDbReference(), db);
            performNewDBOperations(db);

            // Mark this database as fully connected
            db.setAttribute(DBATTRIB_FULLY_CONNECTED, "true");

            // Initially create field mappings. These can only come from csconfig.xml for plugins
            updateFieldMappings(db, null);

            // Add to connected plugins
            connectedPlugins.add(db.getDbReference());

            return db;
        } catch (Throwable e) {
            try {
                db.close();
            } catch (WGAPIException e2) {
                // Silent failure of closing an uninitialized plugin bc. the connection failure is more important
            }
            plugin.setValid(false);
            if (e instanceof Problem) {
                throw (Problem) e;
            } else {
                throw new InvalidPluginException(plugin, "Error connecting plugin", e);
            }
        }

    }

    private void processPublisherOptions(WGDatabase db, Map<String, String> publisherOptions) throws WGException {

        // Put some default options for internal use
        db.setAttribute(DBATTRIB_STORED_QUERIES, new HashMap());
        db.setAttribute(DBATTRIB_META_MAPPINGS, new HashMap());
        db.setAttribute(DBATTRIB_ITEM_MAPPINGS, new HashMap());
        db.setAttribute(DBATTRIB_PLUGIN_SHORTCUTS, new HashMap());

        // Put publisher options
        Iterator<String> optionKeys = publisherOptions.keySet().iterator();
        while (optionKeys.hasNext()) {
            String optionName = optionKeys.next();
            String optionValue = publisherOptions.get(optionName);
            db.setAttribute(optionName, optionValue);
        }

        // Create a file cache object.
        try {
            db.setAttribute(WGACore.DBATTRIB_FILECACHE, new FileCache(db, this));
        } catch (CacheException e) {
            getLog().error("Error initializing file cache for database " + db.getDbReference(), e);
        }
        ;

        // Create a PPR cache object.
        try {
            db.setAttribute(WGACore.DBATTRIB_PPRCACHE, new PostprocessedResourcesCache(db, this));
        } catch (CacheException e) {
            getLog().error("Error initializing PPR cache for database " + db.getDbReference(), e);
        }
        ;

        // CS Only initialisations
        if (db.hasFeature(WGDatabase.FEATURE_FULLCONTENTFEATURES)) {

            // Fetch design encoding
            String currentDesignEncoding = (String) WGUtils
                    .getValueOrDefault(db.getAttribute(DBATTRIB_DESIGN_ENCODING), Charset.defaultCharset().name());

            // design encoding changed
            // this is used for SC_NOT_MODIFIED in WGPDispatcher
            if (getLastDesignEncoding(db.getDbReference()) == null
                    || !getLastDesignEncoding(db.getDbReference()).equals(currentDesignEncoding)) {
                setLastDesignEncoding(db.getDbReference(), currentDesignEncoding);
            }

            // Initialize title path url manager
            try {
                TitlePathManager titlePathManager = new TitlePathManager(db, this,
                        db.getBooleanAttribute(DBATTRIB_TITLEPATHURL, false));
                db.setAttribute(DBATTRIB_TITLEPATHMANAGER, titlePathManager);
            } catch (Exception e) {
                getLog().error("Error initializing title path manager for database " + db.getDbReference(), e);
            }

            // Determine language behaviour
            LanguageBehaviour langBehaviour = createLanguageBehaviour(db);
            db.setAttribute(WGACore.DBATTRIB_LANGUAGEBEHAVIOUR_INSTANCE, langBehaviour);

        }

        // Non-CS only initialisations
        else {
            db.setAttribute(WGACore.DBATTRIB_LANGUAGEBEHAVIOUR_INSTANCE, new OnlyDefaultLanguageBehaviour());
        }

    }

    private LanguageBehaviour createLanguageBehaviour(WGDatabase db) throws WGException {
        String langBehaviourName = (String) db.getAttribute(WGACore.DBATTRIB_LANGUAGEBEHAVIOUR);
        if (langBehaviourName == null) {
            boolean isMultiLanguage = db.getBooleanAttribute(WGACore.DBATTRIB_MULTILANGUAGE_CONTENT, true);
            if (isMultiLanguage) {
                langBehaviourName = StaticLanguageBehaviour.class.getName();
            } else {
                langBehaviourName = OnlyDefaultLanguageBehaviour.class.getName();
            }
        }

        // Convert pre OpenWGA 5.1 values to their 5.1 pendants
        else {
            if (langBehaviourName.equals("default")) {
                langBehaviourName = DynamicLanguageBehaviour.class.getName();
            } else if (langBehaviourName.equals("maincontent")) {
                langBehaviourName = StaticLanguageBehaviour.class.getName();
            } else if (langBehaviourName.equals("browser")) {
                langBehaviourName = OnlyDefaultLanguageBehaviour.class.getName();
            }
        }

        LanguageBehaviour langBehaviour;

        // For applications
        if (getModuleRegistry() != null) {
            ModuleDefinition languageBehaviourMD = getModuleRegistry()
                    .getModuleDefinition(LanguageBehaviourModuleType.class, langBehaviourName);
            if (languageBehaviourMD == null) {
                getLog().error("Unable to load language behaviour " + langBehaviourName
                        + ". Falling back to mode 'static'.");
                languageBehaviourMD = getModuleRegistry().getModuleDefinition(LanguageBehaviourModuleType.class,
                        StaticLanguageBehaviour.class);
            }

            try {
                langBehaviour = (LanguageBehaviour) getModuleRegistry().instantiate(languageBehaviourMD);
            } catch (ModuleInstantiationException e) {
                getLog().error("Exception instantiating language behaviour '"
                        + languageBehaviourMD.getTitle(Locale.getDefault()) + "'. Falling back to mode 'static'.",
                        e);
                langBehaviour = new StaticLanguageBehaviour();
            }
        }

        // For Plugins (no registry yet)
        else {
            try {
                langBehaviour = (LanguageBehaviour) getLibraryLoader().loadClass(langBehaviourName).newInstance();
            } catch (Exception e) {
                getLog().error("Exception instantiating language behaviour '" + langBehaviourName
                        + "'. Falling back to mode 'static'.", e);
                langBehaviour = new StaticLanguageBehaviour();
            }
        }

        // Initialize
        if (langBehaviour instanceof InitializableLanguageBehaviour) {
            ((InitializableLanguageBehaviour) langBehaviour).init(WGA.get(this), db);
        }

        return langBehaviour;
    }

    public Scheduler getScheduler() {
        return _scheduler;
    }

    public static String getBIClientString() {

        return "BI/" + WGAVersion.WGAPUBLISHER_MAJOR_VERSION + "." + WGAVersion.WGAPUBLISHER_MINOR_VERSION + "."
                + WGAVersion.WGAPUBLISHER_MAINTENANCE_VERSION + "_" + WGAVersion.WGAPUBLISHER_BUILD_VERSION;
    }

    public static String getWebformClientString() {

        return "Webform/" + WGAVersion.WGAPUBLISHER_MAJOR_VERSION + "." + WGAVersion.WGAPUBLISHER_MINOR_VERSION
                + "." + WGAVersion.WGAPUBLISHER_MAINTENANCE_VERSION + "_" + WGAVersion.WGAPUBLISHER_BUILD_VERSION;

    }

    public static String getBuildSignature() {
        return _buildSignature;
    }

    /**
     * @param wgaTempDir
     */
    private void killTempDir(File wgaTempDir) {

        Logger.getLogger("wga").info("Deleting temporary files");
        WGUtils.delTree(wgaTempDir);

    }

    /**
     * @return Returns the luceneManager.
     */
    public LuceneManager getLuceneManager() {
        return luceneManager;
    }

    public boolean isLuceneEnabled() {
        return (luceneManager != null);
    }

    /**
     * @deprecated use isAdminLoggedIn(HttpServletRequest) instead
     * @param session
     * @return
     */
    public static boolean isAdminLoggedIn(HttpSession session) {
        String adminName = (String) session.getAttribute(WGACore.SESSION_ADMINNAME);
        return (adminName != null);
    }

    public static boolean isDevelopmentModeEnabled() {
        return Boolean.parseBoolean(System.getProperty(SYSPROPERTY_DEVELOPMENT_MODE, "false"));
    }

    public boolean isAdminLoggedIn(HttpServletRequest request) {
        // check if we should allow passwordless admin login from localhost
        if (request != null && isLocalRequest(request)
                && Boolean.parseBoolean(System.getProperty(SYSPROP_SKIP_LOCAL_ADMIN_LOGINS, "false"))) {
            return true;
        } else if (request != null) {
            String adminName = (String) request.getSession().getAttribute(WGACore.SESSION_ADMINNAME);
            String adminPassword = (String) request.getSession().getAttribute(WGACore.SESSION_ADMINPASSWORD);
            return isAdminLogin(adminName, adminPassword, request);
        } else {
            return false;
        }
    }

    public Analyzer getAnalyzerForLanguageCode(String lang) {
        return analyzerMappings.get(lang);
    }

    public Analyzer getDefaultAnalyzer() {
        return defaultAnalyzer;
    }

    public FileHandler getFileHandlerForExtension(String extension) {
        return fileHandlerMappings.get(extension.toLowerCase());
    }

    public boolean hasFileHandler(String extension) {
        FileHandler handler = getFileHandlerForExtension(extension);
        return handler != null;
    }

    public WGADomain getDomain(Map<String, WGADomain> configs, String name) {

        if (name.startsWith(PluginConfig.PLUGIN_DBKEY_PREFIX)) {
            WGADomain dc = new WGADomain(this, name);
            return dc;
        }

        return configs.get(name.toLowerCase());
    }

    public WGADomain getDomains(String name) {
        // Prevent nullpointer when this is called before domain init in startup
        if (this.domains != null) {
            return getDomain(this.domains, name);
        } else {
            return null;
        }
    }

    public WGADomain getDomainForDatabase(WGDatabase db) {

        String domain = (String) db.getAttribute(DBATTRIB_DOMAIN);
        return getDomains(domain);

    }

    /**
     * @deprecated Use {@link #getDomainForDatabase(WGDatabase)}
     */
    public WGADomain getDomainConfigForDatabase(WGDatabase db) {
        return getDomainForDatabase(db);
    }

    /**
     * @return Returns the quartzScheduler.
     */
    public org.quartz.Scheduler getQuartzScheduler() {
        return _quartzScheduler;
    }

    /**
     * @return Returns the userAgentVerifier.
     */
    public UserAgentVerifier getUserAgentVerifier() {
        return userAgentVerifier;
    }

    private String readOptionValue(Element dbOptionElem) throws IOException {
        String optionValue = dbOptionElem.attributeValue("value");
        String encoding = dbOptionElem.attributeValue("encode");
        if (encoding != null) {
            if (encoding.equals("base64")) {

                optionValue = new String(Base64.decode(optionValue));

            }
        }
        return optionValue;
    }

    /**
     * @deprecated Use {@link #getSymmetricEncryptionEngine()}
     */
    public DESEncrypter getDesEncrypter() {
        return desEncrypter;
    }

    /**
     * @deprecated
     */
    public void setDesEncrypter(DESEncrypter desEncrypter) {
        this.desEncrypter = desEncrypter;
    }

    public SymmetricEncryptionEngine getSymmetricEncryptionEngine() {
        return _symmetricEncryptionEngine;
    }

    public void addEventListener(WGACoreListener listener) {

        if (listener instanceof WGACoreEventListener) {
            WGACoreEventListener coreListener = (WGACoreEventListener) listener;
            if (!coreEventListeners.contains(coreListener)) {
                coreEventListeners.add(coreListener);
            }
        }

        if (listener instanceof WGAConfigurationUpdateListener) {
            WGAConfigurationUpdateListener configListener = (WGAConfigurationUpdateListener) listener;
            if (!configUpdateListeners.contains(configListener)) {
                configUpdateListeners.add(configListener);
            }
        }

    }

    public void removeEventListener(Object listener) {

        if (listener instanceof WGACoreEventListener) {
            coreEventListeners.remove(listener);

        }

        if (listener instanceof WGAConfigurationUpdateListener) {
            configUpdateListeners.remove(listener);
        }

    }

    private void fireCoreEvent(WGACoreEvent event) {

        // Set core status
        if (event.getType() <= WGACoreEvent.WGACORE_STATE_MAXTYPE) {
            _status = event.getType();
        }

        List listenersList = new ArrayList(coreEventListeners);
        Iterator<WGACoreEventListener> listeners = listenersList.iterator();
        while (listeners.hasNext()) {
            WGACoreEventListener listener = listeners.next();
            try {
                switch (event.getType()) {

                case WGACoreEvent.TYPE_CS_CONNECTED:
                    listener.contentStoreConnected(event);
                    break;

                case WGACoreEvent.TYPE_CS_DISCONNECTED:
                    listener.contentStoreDisconnected(event);
                    break;

                case WGACoreEvent.TYPE_STARTUP_PRE_CONNECT:
                    listener.startupPreConnect(event);
                    break;

                case WGACoreEvent.TYPE_STARTUP_POST_CONNECT:
                    listener.startupPostConnect(event);
                    break;

                case WGACoreEvent.TYPE_SHUTDOWN_PRE_DISCONNECT:
                    listener.shutdownPreDisconnect(event);
                    break;

                case WGACoreEvent.TYPE_SHUTDOWN_POST_DISCONNECT:
                    listener.shutdownPostDisconnect(event);
                    break;

                }
            } catch (Exception e) {
                log.error("WGACoreEventListener '" + listener.getClass().getName() + "' failed with exception: "
                        + e.getMessage(), e);
            }
        }

    }

    private void fireConfigEvent(WGAConfigurationUpdateEvent event) {

        List listenersList = new ArrayList(configUpdateListeners);
        Iterator<WGAConfigurationUpdateListener> listeners = listenersList.iterator();
        while (listeners.hasNext()) {
            WGAConfigurationUpdateListener listener = listeners.next();
            try {
                listener.configurationUpdated(event);
            } catch (Exception e) {
                log.error("WGACoreEventListener '" + listener.getClass().getName() + "' failed with exception: "
                        + e.getMessage(), e);
            }
        }

    }

    public WGAResourceBundleManager getResourceBundleManager(WGDatabase database) {

        WGAResourceBundleManager manager = (WGAResourceBundleManager) database
                .getAttribute(DBATTRIB_RESOURCEBUNDLE_MANAGER);
        if (manager == null) {
            synchronized (database) {
                manager = (WGAResourceBundleManager) database.getAttribute(DBATTRIB_RESOURCEBUNDLE_MANAGER);
                if (manager == null) {
                    manager = new WGAResourceBundleManager(database);
                    database.setAttribute(DBATTRIB_RESOURCEBUNDLE_MANAGER, manager);
                }

            }
        }
        return manager;

    }

    public TestCore getTestCore() {
        return _testCore;
    }

    public Locale languageCodeToLocale(String prefLang) {

        if (prefLang == null) {
            return null;
        }

        return WGLanguage.languageNameToLocale(prefLang);
    }

    /**
     * @return Returns the designSyncFileVerifier.
     */
    public DesignFileValidator getDesignFileValidator() {
        return designFileValidator;
    }

    public boolean isClientPermitted(WGDatabase db, HttpServletRequest request) {
        if (db.getAttribute(DBATTRIB_CLIENTRESTRICTIONS) != null) {
            IPAddress ip;
            try {
                ip = IPs.parseIPAddress(request.getRemoteAddr());
                if (!ip.validate()) {
                    log.info("Client ip '" + request.getRemoteAddr()
                            + "' from last request is invalid. Client is not permitted to access db '"
                            + db.getDbReference() + "'.");
                    return false;
                }
            } catch (Exception e) {
                // ip could not be parsed
                log.info("Client ip '" + request.getRemoteAddr()
                        + "' from last request is invalid. Client is not permitted to access db '"
                        + db.getDbReference() + "'.");
                return false;
            }
            boolean permitted = isClientPermitted(db, ip);
            if (!permitted) {
                log.info("Client ip '" + request.getRemoteAddr() + "' was not permitted to access db '"
                        + db.getDbReference() + "'.");
            }
            return permitted;
        } else {
            // if no restrictions client is permitted
            return true;
        }

    }

    public boolean isClientPermitted(WGDatabase db, IPAddress ip) {
        List clientRestrictions = (List) db.getAttribute(DBATTRIB_CLIENTRESTRICTIONS);
        if (clientRestrictions != null) {
            Iterator it = clientRestrictions.iterator();
            while (it.hasNext()) {
                de.innovationgate.utils.net.IPRestriction restriction = (de.innovationgate.utils.net.IPRestriction) it
                        .next();
                if (restriction.exists(ip)) {
                    return true;
                }
            }
            return false;
        } else {
            // if no restrictions client is permitted
            return true;
        }
    }

    /**
     * @return Returns the bruteForceLoginBlocker.
     */
    public BruteForceLoginBlocker getBruteForceLoginBlocker() {
        return bruteForceLoginBlocker;
    }

    /**
     * @return Returns the webserviceEnabled.
     */
    public boolean isWebserviceEnabled() {
        return _webserviceEnabled;
    }

    public boolean isAdministrativePort(int port) {

        // If no admin ports configured administration is open
        if (getWgaConfiguration().getAdminToolsPortRestrictions().size() == 0) {
            return true;
        }

        for (Integer adminPort : getWgaConfiguration().getAdminToolsPortRestrictions()) {
            if (adminPort.equals(port)) {
                return true;
            }
        }

        return false;

    }

    public boolean isAuthoringPort(int port) {

        // If no authoring ports configured authoring is open
        if (getWgaConfiguration().getAuthoringDesignAccessPortRestrictions().size() == 0) {
            return true;
        }

        for (Integer authoringPort : getWgaConfiguration().getAuthoringDesignAccessPortRestrictions()) {
            if (authoringPort.equals(port)) {
                return true;
            }
        }

        return false;

    }

    public String updateConfigDocument(Document newConfig) throws UnsupportedEncodingException,
            FileNotFoundException, IOException, ParserConfigurationException, DocumentException {
        String newTimestamp = WGACore.DATEFORMAT_GMT.format(new Date());
        newConfig.getRootElement().addAttribute("timestamp", newTimestamp);

        OutputFormat outputFormat = OutputFormat.createPrettyPrint();
        outputFormat.setTrimText(true);
        outputFormat.setNewlines(true);

        XMLWriter writer = new XMLWriter(new FileOutputStream(getConfigFile()), outputFormat);
        writer.write(newConfig);
        writer.close();
        return newTimestamp;
    }

    public String getDesignDatabaseKey(WGDatabase db) {

        WGDesignProvider prov = db.getDesignProvider();
        if (prov instanceof DBDesignProvider) {
            return ((DBDesignProvider) prov).getDesignDBKey();
        } else {
            return db.getDbReference();
        }

    }

    public String getDesignDatabaseKey(String dbkey) {

        WGDatabase db = getContentdbs().get(dbkey);
        if (db == null) {
            return dbkey;
        }

        return getDesignDatabaseKey(db);

    }

    /**
     * @return Returns the usageStatistics.
     */
    public WGAUsageStatistics getUsageStatistics() {
        return _usageStatistics;
    }

    public void databaseConnected(WGDatabaseEvent event) {

        getLog().info("Prepared database " + event.getDatabase().getDbReference() + " has been connected");
        _eventManager.updateDatabaseEvents(event.getDatabase());

    }

    /*
     * (non-Javadoc)
     * 
     * @see de.innovationgate.webgate.api.WGDatabaseConnectListener#databaseConnectionError(de.innovationgate.webgate.api.WGDatabaseEvent)
     */
    public void databaseConnectionError(WGDatabaseEvent event) {
        getLog().warn("Removing database " + event.getDatabase().getDbReference()
                + " because of failure of initial connection");

        if (getContentdbs().containsValue(event.getDatabase())) {
            removeContentDB(event.getDatabase().getDbReference());
        } else if (getPersonalisationdbs().containsValue(event.getDatabase())) {
            removePersonalisationDB(getDomainForDatabase(event.getDatabase()).getName());
        }

    }

    public static String getConfigfileName() {

        String fileName = System.getProperty(SYSPROPERTY_CONFIGFILE);
        if (fileName != null) {
            return fileName;
        }

        return CONFIGFILE_NAME;
    }

    public String getCharacterEncoding() {
        if (_characterEncoding == null) {
            // default to UTF-8
            return "UTF-8";
        } else {
            return _characterEncoding;
        }
    }

    public byte[] getLicenseData() throws WGSystemException {
        if (licenseFile != null && licenseFile.exists()) {
            try {
                FileInputStream fin = new FileInputStream(licenseFile);
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                WGUtils.inToOut(fin, out, 1024);
                out.close();
                return out.toByteArray();
            } catch (Exception e) {
                throw new WGSystemException("Error reading license data.", e);
            }
        } else {
            return null;
        }
    }

    /**
     * checks if the given db is available, returns when database is available
     * or timeout is reached
     * 
     * @param dbKey -
     *            db to check for
     * @param interval -
     *            interval in ms for checks
     * @param timeout -
     *            timeout in ms for the method
     * @return true if db is available, false if timeout has been reached
     */
    public boolean waitForDatabase(String dbKey, long interval, long timeout) {
        long startTime = System.currentTimeMillis();
        boolean dbAvailable = getContentdbs().containsKey(dbKey.toLowerCase());
        while (!dbAvailable && ((System.currentTimeMillis() - startTime) < timeout)) {
            try {
                Thread.sleep(interval);
            } catch (InterruptedException e) {
            }
            dbAvailable = getContentdbs().containsKey(dbKey.toLowerCase());
        }
        return dbAvailable;
    }

    public File getConfigFile() {
        return configFile;
    }

    public void updateLibraryLoader() {
        synchronized (libraryClassLoadingChain) {
            _systemContainerManager.updateLibraryLoader(libraryClassLoadingChain);

            // Ensure all TMLScript caches were cleared (#00003829)
            RhinoExpressionEngine tmlScriptEngine = ExpressionEngineFactory.getTMLScriptEngine();
            if (tmlScriptEngine != null) {
                tmlScriptEngine.clearCache();
            }
        }
    }

    public static IsolatedJARLoader getBaseLibraryLoader() {
        return baseLibraryLoader;
    }

    /**
     * Enforces all data in system file container that effects the whole WGA configuration.
     * If the database got the system file container via DBDesignProvider, there is no need to process most of 
     * WGA wide configurations again, since they already were enforced by the design provider. So these operations
     * are only executed when info.isFromProviderDB() is false.
     * @param csConfig
     */
    public void enforceCSConfig(WGDatabase db, ContainerInfo info) {

        // Update library loader. Can be bypassed if the system file container is from a provider DB.
        if (!info.isFromProviderDB()) {
            updateLibraryLoader();
            info.setEnforcedLibraryUpdate(true);
        }

        // If no cxconfig.xml we are finished here
        CSConfig csConfig = info.getCsConfig();
        if (csConfig == null) {
            return;
        }
        CSConfig overlayConfig = info.getOverlayCsConfig();

        // WGA wide configurations, that can be bypassed if the system file container is from a provider DB
        // (because they already were enforced by the provider db)
        if (!info.isFromProviderDB()) {

            // Add encoder mappings
            Iterator encoderMappings = csConfig.getEncoderMappings().iterator();
            while (encoderMappings.hasNext()) {
                EncoderMapping mapping = (EncoderMapping) encoderMappings.next();
                getLog().info("Adding WebTML encoder '" + mapping.getName() + "'");
                if (addEncoderMapping(mapping.getName(), mapping.getImplementationClass(), false)) {
                    info.getEnforcedEncoderMappings().add(mapping.getName());
                }
            }

            if (overlayConfig != null) {
                encoderMappings = overlayConfig.getEncoderMappings().iterator();
                while (encoderMappings.hasNext()) {
                    EncoderMapping mapping = (EncoderMapping) encoderMappings.next();
                    getLog().info("Adding WebTML encoder '" + mapping.getName() + "'");
                    if (addEncoderMapping(mapping.getName(), mapping.getImplementationClass(), false)) {
                        info.getEnforcedEncoderMappings().add(mapping.getName());
                    }
                }
            }

            // Add element mappings
            Iterator elementMappings = csConfig.getElementMappings().iterator();
            while (elementMappings.hasNext()) {
                ElementMapping mapping = (ElementMapping) elementMappings.next();
                getLog().info("Adding WebTML element '" + mapping.getName() + "'");
                if (addElementMapping(mapping.getName(), mapping.getImplementationClass(), false)) {
                    info.getEnforcedElementMappings().add(mapping.getName());
                }
            }

            if (overlayConfig != null) {
                elementMappings = overlayConfig.getElementMappings().iterator();
                while (elementMappings.hasNext()) {
                    ElementMapping mapping = (ElementMapping) elementMappings.next();
                    getLog().info("Adding WebTML element '" + mapping.getName() + "'");
                    if (addElementMapping(mapping.getName(), mapping.getImplementationClass(), false)) {
                        info.getEnforcedElementMappings().add(mapping.getName());
                    }
                }
            }

            // Add media keys
            Iterator mediaKeys = csConfig.getMediaKeys().iterator();
            while (mediaKeys.hasNext()) {
                de.innovationgate.wga.common.beans.csconfig.v1.MediaKey mediaKey = (de.innovationgate.wga.common.beans.csconfig.v1.MediaKey) mediaKeys
                        .next();
                getLog().info("Adding WebTML media key '" + mediaKey.getKey() + "' for MIME type '"
                        + mediaKey.getMimeType() + "'");
                addMediaMapping(mediaKey, false);
                info.getEnforcedMediaMappings().add(mediaKey.getKey());
            }

            if (overlayConfig != null) {
                mediaKeys = csConfig.getMediaKeys().iterator();
                while (mediaKeys.hasNext()) {
                    de.innovationgate.wga.common.beans.csconfig.v1.MediaKey mediaKey = (de.innovationgate.wga.common.beans.csconfig.v1.MediaKey) mediaKeys
                            .next();
                    getLog().info("Adding WebTML media key '" + mediaKey.getKey() + "' for MIME type '"
                            + mediaKey.getMimeType() + "'");
                    addMediaMapping(mediaKey, false);
                    info.getEnforcedMediaMappings().add(mediaKey.getKey());
                }
            }

            if (csConfig instanceof de.innovationgate.wga.common.beans.csconfig.v2.CSConfig) {

                // Add TMLScript global shortcuts
                de.innovationgate.wga.common.beans.csconfig.v2.CSConfig v2 = (de.innovationgate.wga.common.beans.csconfig.v2.CSConfig) csConfig;
                Iterator shortcuts = v2.getShortcuts().iterator();
                while (shortcuts.hasNext()) {
                    Shortcut shortcut = (Shortcut) shortcuts.next();
                    if (shortcut.getType() == Shortcut.TYPE_TMLSCRIPT_GLOBAL) {
                        try {
                            getTmlscriptGlobalRegistry().registerGlobal(ExpressionEngineFactory.getTMLScriptEngine()
                                    .createGlobal(shortcut.getShortcut(), TMLScriptGlobal.TYPE_PACKAGE_OR_CLASS,
                                            shortcut.getReference()));
                        } catch (WGException e) {
                            getLog().error("Exception instantiating TMLScript global: " + shortcut.getShortcut());
                        }
                    }
                }

            }

            if (overlayConfig instanceof de.innovationgate.wga.common.beans.csconfig.v2.CSConfig) {

                // Add TMLScript global shortcuts
                de.innovationgate.wga.common.beans.csconfig.v2.CSConfig v2 = (de.innovationgate.wga.common.beans.csconfig.v2.CSConfig) overlayConfig;
                Iterator shortcuts = v2.getShortcuts().iterator();
                while (shortcuts.hasNext()) {
                    Shortcut shortcut = (Shortcut) shortcuts.next();
                    if (shortcut.getType() == Shortcut.TYPE_TMLSCRIPT_GLOBAL) {
                        try {
                            getTmlscriptGlobalRegistry().registerGlobal(ExpressionEngineFactory.getTMLScriptEngine()
                                    .createGlobal(shortcut.getShortcut(), TMLScriptGlobal.TYPE_PACKAGE_OR_CLASS,
                                            shortcut.getReference()));
                        } catch (WGException e) {
                            getLog().error("Exception instantiating TMLScript global: " + shortcut.getShortcut());
                        }
                    }
                }

            }

        }

        // Set WGA version compliance, either being overridden by the overlay or take it from the base
        VersionCompliance versionCompliance = null;
        if (versionCompliance == null) {
            versionCompliance = VersionCompliance.get(csConfig.getVersionCompliance());
        }

        Set firstLevelOptions = (Set) db.getAttribute(DBATTRIB_FIRSTLEVELDBOPTIONS);
        db.enforceVersionCompliance(versionCompliance.getComplianceString(),
                !firstLevelOptions.contains(WGDatabase.COPTION_NOITEMBEHAVIOUR));

        // Add jobs
        if (!isPluginInitDisabled(db, csConfig)) {
            addCsConfigJobs(db, info, csConfig.getJobDefinitions().iterator());
        }

        if (overlayConfig != null && !isPluginInitDisabled(db, overlayConfig)) {
            addCsConfigJobs(db, info, overlayConfig.getJobDefinitions().iterator());
        }

    }

    public boolean isPluginInitDisabled(WGDatabase db, CSConfig csConfig) {
        if (csConfig.getPluginConfig() instanceof de.innovationgate.wga.common.beans.csconfig.v3.PluginConfig) {
            de.innovationgate.wga.common.beans.csconfig.v3.PluginConfig v3Config = (de.innovationgate.wga.common.beans.csconfig.v3.PluginConfig) csConfig
                    .getPluginConfig();
            if (v3Config.isDisablePluginInit() && db.getDbReference().startsWith("plugin-")) {
                return true;
            }
        }
        return false;
    }

    protected void addCsConfigJobs(WGDatabase db, ContainerInfo info, Iterator jobs) {
        while (jobs.hasNext()) {
            JobDefinition job = (JobDefinition) jobs.next();
            String jobName = db.getDbReference() + "." + job.getName();
            try {
                Task task = null;

                if (job.getType() == JobDefinition.TYPE_TMLSCRIPTMODULE) {
                    ScriptTask scriptTask = new ScriptTask();
                    scriptTask.setCancelJobOnFail(true);
                    scriptTask.setDatabase(db.getDbReference());
                    scriptTask.setModule(job.getResource());
                    task = scriptTask;
                } else if (job.getType() == JobDefinition.TYPE_JAVA) {
                    JavaTask javaTask = new JavaTask();
                    javaTask.setClassName(job.getResource());
                    task = javaTask;
                } else {
                    getLog().error("Error adding job '" + jobName + "'. Unknown job type: " + job.getType());
                    continue;
                }

                task.setDescription(job.getDescription());

                JobSchedule schedule = null;
                if (job.getSchedule() != null && !job.getSchedule().trim().equals("")) {
                    schedule = new JobSchedule();
                    schedule.setEnabled(true);
                    schedule.setScheduleData(job.getSchedule());
                    schedule.setType(JobSchedule.TYPE_CRON);
                }

                Job schedulerJob = getScheduler().addCustomTaskJob(jobName, task, false, schedule);
                schedulerJob.setDescription(job.getDescription());
                schedulerJob.getOptions().put("database", db.getDbReference());
                info.getEnforcedJobDefinitions().add(schedulerJob.getName());
            } catch (Exception e) {
                getLog().error("Error adding job '" + jobName + "' from content store configuration", e);
            }
        }
    }

    protected boolean addMediaMapping(MediaKey mediaKey, boolean system) {
        if (system) {
            this.systemMediaKeys.put(mediaKey.getKey(), mediaKey);
            return true;
        } else if (systemMediaKeys.containsKey(mediaKey.getKey())) {
            MediaKey key = systemMediaKeys.get(mediaKey.getKey());
            if (!key.equals(mediaKey)) {
                getLog().warn("Cannot add media key '" + mediaKey.getKey()
                        + "' because the key is already used in WGA configuration with different data");
            }
            return false;
        } else {
            this.customMediaKeys.put(mediaKey.getKey(), mediaKey);
            return true;
        }
    }

    public InputStream dumpContentStore(WGDatabase dbSource, String filterExpression, boolean autoCorrect,
            Logger log) throws WGAPIException, IOException {
        return dumpContentStore(dbSource, filterExpression, autoCorrect, log, false);
    }

    public InputStream dumpContentStore(WGDatabase dbSource, String filterExpression, boolean autoCorrect,
            Logger log, boolean includeACL) throws WGAPIException, IOException {
        return dumpContentStore(dbSource, filterExpression, autoCorrect, log, includeACL, false);
    }

    public InputStream dumpContentStore(WGDatabase dbSource, String filterExpression, boolean autoCorrect,
            Logger log, boolean includeACL, boolean includeSystemAreas) throws WGAPIException, IOException {
        return dumpContentStore(dbSource, filterExpression, autoCorrect, log, includeACL, includeSystemAreas, true);
    }

    public InputStream dumpContentStore(WGDatabase dbSource, String filterExpression, boolean autoCorrect,
            Logger log, boolean includeACL, boolean includeSystemAreas, boolean includeArchived)
            throws WGAPIException, IOException {

        log.info("Creating dump database");

        // Create working folder for dump database
        File dir = File.createTempFile("csd", ".tmp", WGFactory.getTempDir());
        dir.delete();
        dir.mkdir();

        // Create dump database
        Map<String, String> options = new HashMap<String, String>();
        options.put(WGDatabase.COPTION_MONITORLASTCHANGE, "false");
        options.put(WGDatabaseImpl.COPTION_DISTINCTFILECONTENTS, "false");
        WGDatabase dbTarget = WGFactory.getInstance().openDatabase(null,
                de.innovationgate.webgate.api.hsql.WGDatabaseImpl.class.getName(), dir.getAbsolutePath() + "/wgacs",
                "sa", null, options);
        dbTarget.setDbReference("Temporary WGACS dump target");

        // Replicate
        log.info("Synchronizing data to dump database");
        dbSource.lock();
        try {
            ContentStoreDumpManager importer = new ContentStoreDumpManager(dbSource, dbTarget, log);
            importer.exportDump(includeACL, includeSystemAreas, includeArchived);
        } finally {
            dbSource.unlock();
        }

        // Close database and zip up its contents
        log.info("Creating dump file");
        dbTarget.close();
        File zipFile = File.createTempFile("csz", ".tmp", WGFactory.getTempDir());
        ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile)));
        out.setLevel(9);
        File[] files = dir.listFiles();
        for (int i = 0; i < files.length; i++) {
            File file = files[i];
            out.putNextEntry(new ZipEntry(file.getName()));
            InputStream in = new BufferedInputStream(new FileInputStream(file));
            WGUtils.inToOut(in, out, 2048);
            in.close();
            out.closeEntry();
        }
        out.close();

        // Delete temp dir
        WGUtils.delTree(dir);

        // Return input stream for zip file
        return new TempFileInputStream(zipFile);

    }

    public String runTransientTask(String taskImplementation, String taskName, Map options)
            throws ClassNotFoundException, InstantiationException, IllegalAccessException, JobFailedException,
            ConfigurationException {

        Class clazz = getLibraryLoader().loadClass(taskImplementation);
        if (!Task.class.isAssignableFrom(clazz)) {
            throw new IllegalArgumentException("Class " + taskName + " does not implement " + Task.class.getName());
        }

        Task task = (Task) clazz.newInstance();

        Job job = getScheduler().addCustomTaskJob(taskName, task, true, null);
        job.getOptions().putAll(options);
        task.configure(this);

        getScheduler().run(job.getName(), "WGA Scheduler Custom Task Runner", options, null);
        return job.getName();

    }

    public boolean importContentStoreDump(ZipInputStream zipIn, WGDatabase targetDB, Logger log)
            throws IOException, WGAPIException {
        return importContentStoreDump(zipIn, targetDB, log, false);
    }

    public void importContentStoreDump(ZipInputStream zipIn, WGDatabase targetDB)
            throws IOException, WGAPIException {
        importContentStoreDump(zipIn, targetDB, null);
    }

    public boolean importContentStoreDump(ZipInputStream zipIn, WGDatabase targetDB, Logger log, boolean includeACL)
            throws IOException, WGAPIException {
        return importContentStoreDump(zipIn, targetDB, log, includeACL, false);
    }

    public boolean importContentStoreDump(ZipInputStream zipIn, WGDatabase targetDB, Logger log, boolean includeACL,
            boolean includeSystemAreas) throws IOException, WGAPIException {

        if (log != null) {
            log.info("Extracting dump data");
        }

        // Create working folder for dump database
        File dir = File.createTempFile("csd", ".tmp", WGFactory.getTempDir());
        dir.delete();
        dir.mkdir();

        // Extract dump to folder
        ZipEntry entry = zipIn.getNextEntry();
        while (entry != null) {
            FileOutputStream out = new FileOutputStream(new File(dir, entry.getName()));
            WGUtils.inToOut(zipIn, out, 2048);
            out.close();

            entry = zipIn.getNextEntry();
        }

        // Open dump database
        Map<String, String> options = new HashMap<String, String>();
        options.put(WGDatabase.COPTION_MONITORLASTCHANGE, "false");
        WGDatabase sourceDB = WGFactory.getInstance().openDatabase(null,
                de.innovationgate.webgate.api.hsql.WGDatabaseImpl.class.getName(), dir.getAbsolutePath() + "/wgacs",
                null, null, options);
        sourceDB.setDbReference("WGA Content Store Initial data dump");

        if (log != null) {
            log.info("Importing dump data");
        }

        // Do the dump
        ContentStoreDumpManager importer = new ContentStoreDumpManager(sourceDB, targetDB, log);
        boolean result = importer.importDump(includeACL, includeSystemAreas);

        // Close dump database
        sourceDB.close();

        return result;
    }

    protected static Map<String, String> getDbDefaultAttributes() {
        return _dbDefaultAttributes;
    }

    /**
     * stores the given licenses data as wga.skey in config path and do necessary updates on wga.xml to reflect new license file
     * @param licenseData
     * @throws WGSystemException 
     */
    /*
    public void storeLicenseData(byte[] licenseData) throws WGSystemException {
    try {            
        File licenseFile = new File(configFile.getParentFile(), LICENSE_FILENAME);
                        
        if (licenseFile.exists()) {
            licenseFile.createNewFile();
        }
        ByteArrayInputStream bin = new ByteArrayInputStream(licenseData);
        FileOutputStream fout = new FileOutputStream(licenseFile);
        WGUtils.inToOut(bin, fout, 1024);
        fout.close();
        log.info("New license data successfully saved in license file '" + licenseFile.getAbsolutePath()  + "'.");
                        
        // update configuration
        updateConfig();            
    }
    catch (Exception e) {
        log.error("Unable to store license data.", e);
        throw new WGSystemException("Unable to store license data.", e);
    }
        
    }*/

    private void loadOptions(Map<String, String> thePublisherOptions, Element publisherOptionElements) {
        Iterator publisherOptionsIt = publisherOptionElements.elementIterator("option");
        while (publisherOptionsIt.hasNext()) {
            Element publisherOptionElem = (Element) publisherOptionsIt.next();
            try {
                thePublisherOptions.put(publisherOptionElem.attributeValue("name"),
                        readOptionValue(publisherOptionElem));
            } catch (IOException e) {
                getLog().error("Exception decoding option '" + publisherOptionElem.attributeValue("name") + "'", e);
            }
        }
    }

    public void removeCSConfig(WGDatabase database, ContainerInfo info, boolean finalRemove) {

        /* This is not safe. The media key may be defined by multiple designs.
        Iterator<String> mediaMappings = info.getEnforcedMediaMappings().iterator();
        while (mediaMappings.hasNext()) {
        String mediaKey = mediaMappings.next();
        this.customMediaKeys.remove(mediaKey);
        }*/

        Iterator<String> elementMappings = info.getEnforcedElementMappings().iterator();
        while (elementMappings.hasNext()) {
            String elementName = elementMappings.next();
            this.customElements.remove(elementName);
        }

        Iterator<String> encoderMappings = info.getEnforcedEncoderMappings().iterator();
        while (encoderMappings.hasNext()) {
            String elementName = encoderMappings.next();
            _customFormatters.remove(elementName.toLowerCase());
        }

        Iterator<String> jobDefinitions = info.getEnforcedJobDefinitions().iterator();
        while (jobDefinitions.hasNext()) {
            String jobName = jobDefinitions.next();
            getScheduler().removeJob(jobName);
        }

        // We only do this if the db is really removed.
        // If not, and this is only an info update, the library loader would get updated
        // right after this method and we can spare the overhead here
        if (finalRemove && info.isEnforcedLibraryUpdate()) {
            if (!info.isStaticClasspath() && getLibraryClassLoadingChain().hasSubLoader(info.getDbkey())) {
                getLog().info(
                        "Removing libraries of database " + info.getDbkey() + " from WGA java library loader");
                try {
                    getLibraryClassLoadingChain().removeSubLoader(info.getDbkey());
                } catch (IllegalStateException e) {
                    getLog().warn("The libraries of database " + info.getDbkey()
                            + " cannt be removed from WGA java library loader as they are marked static");
                }
            }
            updateLibraryLoader();
        }

        // Remove module definitions (May be independent of any classpath)
        getModuleRegistry().removeDefinitionsFromLoader(info.getDbkey());
    }

    public SystemContainerManager getSystemContainerManager() {
        return _systemContainerManager;
    }

    public boolean logout(String domain, javax.servlet.http.HttpSession session,
            HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, boolean fireEvent)
            throws WGException {

        // Remove the sessionLogin for this domain
        Map<Object, DBLoginInfo> sessionLogins = getSessionLogins(session);
        DBLoginInfo oldLoginInfo = null;
        if (domain != null) {
            oldLoginInfo = sessionLogins.remove(domain);
        } else {
            sessionLogins.clear();
            session.removeAttribute("$defaultlogin");
        }

        // Remove profiles of self-personalized dbs, fire events
        WGA wga = WGA.get(httpServletRequest, httpServletResponse, this);
        for (WGDatabase db : getDatabasesForDomain(domain)) {
            int persMode = Integer.parseInt((String) readPublisherOptionOrDefault(db, WGACore.DBATTRIB_PERSMODE));
            if (persMode == Constants.PERSMODE_LOGIN) {
                session.removeAttribute(
                        PersonalisationManager.SESSION_PROFILENAME_INDIVIDUALDB + db.getDbReference());
            }
            if (fireEvent && oldLoginInfo != null && !WGDatabase.ANONYMOUS_USER.equals(oldLoginInfo.getUserName())
                    && db.hasFeature(WGDatabase.FEATURE_FULLCONTENTFEATURES)) {
                wga.app(db).createEvent("auth=logout").param("userName", oldLoginInfo.getUserName())
                        .param("sessionId", session.getId()).param("authType", oldLoginInfo.getAuthenticationType())
                        .fireOnSession();
            }
        }

        return true;
    }

    public boolean login(String user, Object password, String domain, HttpServletRequest request,
            HttpServletResponse response) throws WGException {
        // Get the domain configuration
        WGADomain domainConfig = getDomains(domain);
        if (domainConfig == null) {
            throw new LoginException("The domain '" + domain + "' does not exist");
        }

        // Do login. Either on domain's auth module or on a DB of the domain
        boolean isLoginSuccessful = false;
        if (domainConfig.getAuthModule() != null) {
            // Do a login on the domain's auth module
            try {
                AuthenticationSession authSession = getBruteForceLoginBlocker().login(domainConfig, user, password);
                if (authSession != null) {
                    isLoginSuccessful = true;
                }
            } catch (AuthenticationException e) {
                throw new LoginException("Unable to login on domain '" + domain + "'.", e);
            }

        } else {
            // Find a database of the requested domain
            Collection<WGDatabase> dbObjs = getContentdbs().values();
            if (dbObjs.isEmpty()) {
                return false;
            }

            Iterator<WGDatabase> dbs = getContentdbs().values().iterator();
            WGDatabase db = null;
            int accessLevel = WGDatabase.ACCESSLEVEL_NOTLOGGEDIN;

            // Do a login on the first database found that belongs to this domain
            while (dbs.hasNext()) {
                db = dbs.next();
                String compareDomain = (String) db.getAttribute(WGACore.DBATTRIB_DOMAIN);
                if (compareDomain != null && compareDomain.equalsIgnoreCase(domain)) {
                    accessLevel = getBruteForceLoginBlocker().login(db, user, password);
                    if (accessLevel > WGDatabase.ACCESSLEVEL_NOTLOGGEDIN) {
                        isLoginSuccessful = true;
                        if (db.isSessionOpen()) {
                            setSessionCookie(request, response, db);
                        }
                        break;
                    } else {
                        // unable to login to db of domain
                        // do not try to login to further dbs in this domain bc.
                        // - each db of a domain must support the same auth information
                        // - we will block the login to early bc. each further login request are counted by bruteForceLoginBlocker
                        isLoginSuccessful = false;
                        break;
                    }
                }
            }
        }

        // React on login success
        if (isLoginSuccessful) {

            // First do a logout for sure
            logout(domain, request.getSession(), request, response, true);

            getSessionLogins(request.getSession()).put(domain,
                    new DBLoginInfo(user, password, DBLoginInfo.AuthType.PASSWORD));

            WGA wga = WGA.get(request, response, this);

            // F00004852
            // perform a reopenSession on all opened databases of this domain, fire application event
            Iterator<WGDatabase> dbsInDomain = getDatabasesForDomain(domain).iterator();
            while (dbsInDomain.hasNext()) {
                WGDatabase db = dbsInDomain.next();
                if (db.isSessionOpen()) {
                    db.reopenSession(user, password);
                }
                if (db.hasFeature(WGDatabase.FEATURE_FULLCONTENTFEATURES)) {
                    wga.app(db).createEvent("auth=login").param("userName", user)
                            .param("authType", DBLoginInfo.AuthType.PASSWORD).fireOnSession();
                }
            }

            return true;
        } else {
            return false;
        }
    }

    protected void setSessionCookie(javax.servlet.http.HttpServletRequest request,
            javax.servlet.http.HttpServletResponse response, WGDatabase database) {
        try {
            if (!database.getSessionContext().isAnonymous()
                    && database.hasFeature(WGDatabase.FEATURE_SESSIONTOKEN)) {
                String cookieName = (String) database.getAttribute(WGACore.DBATTRIB_SESSIONCOOKIE);
                String sessionToken = database.getSessionContext().getSessionToken();
                if (cookieName != null && sessionToken != null && !sessionToken
                        .equals(request.getSession().getAttribute(WGACore.SESSION_COOKIESET + cookieName))) {

                    WGCookie sessionCookie = new WGCookie(cookieName, sessionToken);
                    sessionCookie.setPath("/");
                    String sessionCookieDomain = (String) database
                            .getAttribute(WGACore.DBATTRIB_SESSIONCOOKIEDOMAIN);
                    if (sessionCookieDomain != null) {
                        sessionCookie.setDomain(sessionCookieDomain);
                    } else {
                        sessionCookie.setDomain(getSecondLevelDomain(request.getServerName()));
                    }
                    sessionCookie.addCookieHeader(response);
                    request.getSession().setAttribute(WGACore.SESSION_COOKIESET + cookieName, sessionToken);
                }
            }
        } catch (WGBackendException e) {
            getLog().error("Error setting session cookie", e);
        }
    }

    private String getSecondLevelDomain(String serverName) {

        int noOfPoints = WGUtils.countOccurences(serverName, ".");

        // If we have less than three parts we cannot cutoff subdomains
        if (noOfPoints < 2) {
            return serverName;
        }

        int tldPos = serverName.lastIndexOf(".");
        int sldPos = serverName.lastIndexOf(".", tldPos - 1);

        return serverName.substring(sldPos);

    }

    public void disconnectPlugin(WGAPlugin plugin) {
        String dbKey = plugin.buildDatabaseKey();
        WGDatabase db = this.contentdbs.get(dbKey);
        if (db != null) {
            getLog().info("Disconnecting plugin " + plugin.getPluginID().getUniqueName() + " Version "
                    + plugin.getPluginID().getVersion().toString());
            removeContentDB(dbKey);
        }
        getProblemRegistry().clearProblemScope(new PluginScope(plugin.getPluginID().getUniqueName()));

    }

    public WGAPluginSet getPluginSet() {
        return pluginSet;
    }

    public File getWgaDataDir() {
        return _wgaDataDir;
    }

    public List<WGAFilterConfig> getFilterMappings() {
        return filterMappings;
    }

    public TMLScriptGlobalRegistry getTmlscriptGlobalRegistry() {
        return _tmlscriptGlobalRegistry;
    }

    public int getDefaultPort(WGDatabase db, String protocol) {

        // Retrieve from publisher option
        String strProtocol = (String) db.getAttribute(DBATTRIB_DEFAULTPORT_BASE + protocol.toUpperCase());
        if (strProtocol != null) {
            try {
                int port = Integer.parseInt(strProtocol);
                return port;
            } catch (NumberFormatException e) {
                getLog().error("Default port of '" + protocol + "' for database '" + db.getDbReference()
                        + "' not numeric: " + strProtocol);
            }
        }

        // Default ports
        Integer defaultPort = URLBuilder.getDefaultPortForProtocol(protocol);
        if (defaultPort != null) {
            return defaultPort.intValue();
        }

        // Fallback
        return 80;

    }

    /*
    public ResourceConfiguration getResourceConfiguration() {
    return _resourceConfiguration;
    }*/

    /**
     * retrieves the global wga mail configuration
     * might be null if not configured in detail mailhost is not set in wgaxml
     * @return
     */
    public WGAMailConfiguration getMailConfig() {
        return _mailConfig;
    }

    public void send(WGAMailNotification notification) {
        WGAMailConfiguration config = getMailConfig();

        if (config != null && config.isEnableAdminNotifications()) {
            try {
                Message msg = new MimeMessage(config.createMailSession());

                // set recipient and from address
                String toAddress = config.getToAddress();
                if (toAddress == null) {
                    getLog().error(
                            "Unable to send wga admin notification because no recipient address is configured");
                    return;
                }

                msg.setRecipient(Message.RecipientType.TO, new InternetAddress(toAddress));
                InternetAddress[] fromAddr = new InternetAddress[1];
                fromAddr[0] = new InternetAddress(config.getFromAddress());
                msg.addFrom(fromAddr);

                msg.setSentDate(new Date());

                InetAddress localMachine = InetAddress.getLocalHost();
                String hostname = localMachine.getHostName();
                String serverName = getWgaConfiguration().getServerName();
                if (serverName == null) {
                    serverName = hostname;
                }

                msg.setSubject(notification.getSubject());

                msg.setHeader(WGAMailNotification.HEADERFIELD_TYPE, notification.getType());

                MimeMultipart content = new MimeMultipart();
                MimeBodyPart body = new MimeBodyPart();

                StringBuffer strBody = new StringBuffer();
                strBody.append("<html><head></head><body style=\"color:#808080\">");
                strBody.append(notification.getMessage());
                String rootURL = getWgaConfiguration().getRootURL();
                if (rootURL != null) {
                    //strBody.append("<br><br>");
                    strBody.append("<p><a href=\"" + rootURL + "/plugin-admin\">" + WGABrand.getName()
                            + " admin client ...</a></p>");
                }
                // append footer
                strBody.append("<br><br><b>System information:</b><br><br>");
                strBody.append("<b>Server:</b> " + serverName + " / " + WGACore.getReleaseString() + "<br>");
                strBody.append("<b>Host:</b> " + hostname + "<br>");
                strBody.append("<b>Operation System:</b> " + System.getProperty("os.name") + " Version "
                        + System.getProperty("os.version") + " (" + System.getProperty("os.arch") + ")<br>");
                strBody.append("<b>Java virtual machine:</b> " + System.getProperty("java.vm.name") + " Version "
                        + System.getProperty("java.vm.version") + " (" + System.getProperty("java.vm.vendor")
                        + ")");

                strBody.append("</body></html>");
                body.setText(strBody.toString());
                body.setHeader("MIME-Version", "1.0");
                body.setHeader("Content-Type", "text/html");

                content.addBodyPart(body);
                AppLog appLog = WGA.get(this).service(AppLog.class);

                if (notification.isAttachLogfile()) {
                    MimeBodyPart attachmentBody = new MimeBodyPart();

                    StringWriter applog = new StringWriter();
                    int applogSize = appLog.getLinesCount();
                    int offset = applogSize - notification.getLogfileLines();
                    if (offset < 0) {
                        offset = 1;
                    }
                    appLog.writePage(offset, notification.getLogfileLines(), applog, LogLevel.LEVEL_INFO, false);

                    attachmentBody.setDataHandler(new DataHandler(applog.toString(), "text/plain"));
                    attachmentBody.setFileName("wga.log");
                    content.addBodyPart(attachmentBody);
                }
                msg.setContent(content);

                // Send mail
                Thread mailThread = new Thread(new AsyncMailSender(msg), "WGAMailSender");
                mailThread.start();
            } catch (Exception e) {
                getLog().error("Unable to send wga admin notification.", e);
            }
        }
    }

    private class AsyncMailSender implements Runnable {

        private Message _message;

        public AsyncMailSender(Message message) {
            _message = message;
        }

        public void run() {
            try {
                Transport.send(_message);
            } catch (MessagingException e) {
                getLog().error("Unable to send wga notification email.", e);
            }
        }

    }

    public static class DetermineDatabaseDefaultLanguageProblemOccasion implements ProblemOccasion {

        private String _dbkey;
        private DatabaseScope _scope;

        public DetermineDatabaseDefaultLanguageProblemOccasion(String dbkey) {
            _dbkey = dbkey;
            _scope = new DatabaseScope(dbkey);
        }

        @Override
        public boolean isClearedAutomatically() {
            return true;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((_dbkey == null) ? 0 : _dbkey.hashCode());
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            ConnectDatabaseProblemOccasion other = (ConnectDatabaseProblemOccasion) obj;
            if (_dbkey == null) {
                if (other._dbkey != null)
                    return false;
            } else if (!_dbkey.equals(other._dbkey))
                return false;
            return true;
        }

        @Override
        public ProblemScope getDefaultScope() {
            return _scope;
        }

        @Override
        public Class<? extends ProblemType> getDefaultType() {
            return AdministrativeProblemType.class;
        }

        @Override
        public Class<?> getDefaultRefClass() {
            return WGACore.class;
        }

    }

    public class WGAServerOptionReader implements ModuleRegistryChangeListener {

        private Map<String, OptionDefinition> _optionDefCache = new ConcurrentHashMap<String, OptionDefinition>();

        public Object readServerOptionOrDefault(String name) {

            // Fetch option definition
            OptionDefinition optionDef = _optionDefCache.get(name);
            if (optionDef == null) {
                for (ModuleDefinition modDef : WGACore.this.getModuleRegistry()
                        .getModulesForType(WGAServerOptionsModuleType.class).values()) {
                    optionDef = modDef.getOptionDefinitions().get(name);
                    if (optionDef != null) {
                        break;
                    }
                }

                if (optionDef == null) {
                    optionDef = new DefaultOptionDefinition(name);
                }

                _optionDefCache.put(name, optionDef);
            }

            // Read the option value, default it if neccessary
            String value = getWgaConfiguration().getServerOptions().get(name);
            if (value == null && optionDef.getDefaultValue() != null) {
                value = optionDef.getDefaultValue();
            }

            try {
                return OptionReader.unconvertOptionValue(optionDef, value);
            } catch (Exception e) {
                throw new RuntimeException(
                        "Exception unconverting server option '" + name + "' with value '" + value + "'", e);
            }

        }

        @Override
        public void moduleRegistryChanged(ModuleRegistry registry, Class<? extends ModuleType> moduleType) {
            _optionDefCache.clear();
        }

    }

    public File getLoggingDir() {
        return loggingDir;
    }

    public static List<WGLanguage> getLanguageDefinitions(WGDatabase database, Collection langKeys)
            throws WGAPIException {

        List<WGLanguage> langs = new ArrayList<WGLanguage>();
        Iterator keys = langKeys.iterator();
        while (keys.hasNext()) {
            String key = (String) keys.next();
            WGLanguage lang = database.getLanguage(key);
            if (lang != null) {
                langs.add(lang);
            }
        }

        return langs;

    }

    public int getStatus() {
        return _status;
    }

    public WGAConfiguration getWgaConfiguration() {
        return _wgaConfiguration;
    }

    public String getErrorPage() {
        return _errorPage;
    }

    public WGADesignManager getDesignManager() {
        return _designManager;
    }

    public void logCategoryInfo(String msg, int level) {
        WGUtils.logCategoryInfo(getLog(), msg, level);
    }

    public Cache getDesignFileCache() {
        return designFileCache;
    }

    public synchronized void saveWgaConfiguration(final WGAConfiguration config) throws Exception {

        try {

            // Prevent this in dev studio to reduce VCS conflicts
            if (!isDevelopmentModeEnabled()) {
                config.setLastModified(new Date());
            }

            // First write to buffer, so serialisation/validation errors are catched before it is actually written to disk
            ByteArrayOutputStream buffOut = new ByteArrayOutputStream();
            WGAConfiguration.write(config, buffOut);

            // Write buffered output to file
            FileOutputStream configOut = new FileOutputStream(configFile);
            configOut.write(buffOut.toByteArray());
            configOut.flush();
            configOut.close();

            // enforce configuration
            updateConfig();

        } catch (ConfigValidationException e) {
            getLog().error("Configuration update failed because of validation errors:");
            Iterator<ValidationError> errors = e.getValidationErrors().iterator();
            while (errors.hasNext()) {
                ValidationError validationError = (ValidationError) errors.next();
                getLog().error(validationError.getMessage());
            }
            throw e;
        }

    }

    public WGAConfiguration cloneWgaConfiguration() {
        return (WGAConfiguration) XStreamUtils.clone(_wgaConfiguration);
    }

    public void setFilter(WGAFilter filter) {
        _filter = filter;
    }

    public WGAFilter getFilter() {
        return _filter;
    }

    public Map<String, WGDatabaseServer> getDatabaseServers() {
        return _databaseServers;
    }

    public List<String> getEncodingFormatterNames() {

        List<String> formatters = new ArrayList<String>();
        formatters.addAll(_customFormatters.keySet());
        formatters.addAll(_systemFormatters.keySet());

        // Embedded system formatters - that are served from outside the formatters registry

        formatters.add(ENCODER_CRLF);
        formatters.add(ENCODER_HTML);
        formatters.add(ENCODER_RTF);
        formatters.add(ENCODER_RTFSYSTEM);
        formatters.add(ENCODER_XML);
        formatters.add(ENCODER_JAVASCRIPT);
        formatters.add(ENCODER_JSON);
        formatters.add(ENCODER_NAMEPART);
        formatters.add(ENCODER_PLAINTEXT);
        formatters.add(ENCODER_URL);
        formatters.add(ENCODER_URLQUERY);
        formatters.add(ENCODER_NONE);
        return formatters;

    }

    public WebTMLCache getWebTMLCache() {
        return _webTMLCache;
    }

    public Map<String, WGADomain> getDomains() {
        return domains;
    }

    public static DynamicClassLoadingChain getLibraryClassLoadingChain() {
        return libraryClassLoadingChain;
    }

    public WGALoggerWrapper getAccessLogger() {
        return _accessLogger;
    }

    public Map<String, ShareDefinition> getShares() {
        return _shares;
    }

    public void addCustomShare(ShareDefinition def) throws ShareInitException {
        def.setOrigin(ShareDefinition.ORIGIN_CUSTOM);
        getShares().put(def.getName(), def);
        getLog().info("Added custom share '" + def.getName() + "'");
    }

    public Object readPublisherOptionOrDefault(WGDatabase db, String name) {

        if (_publisherOptionsReader != null) {
            return _publisherOptionsReader.readPublisherOptionOrDefault(db, name);
        } else {
            return db.getAttribute(name);
        }

    }

    public Object readServerOptionOrDefault(String name) {
        if (_serverOptionsReader != null) {
            return _serverOptionsReader.readServerOptionOrDefault(name);
        } else {
            return getWgaConfiguration().getServerOptions().get(name);
        }
    }

    public WGAURLBuilder retrieveURLBuilder(HttpServletRequest request, WGDatabase db) {
        return retrieveURLBuilder(request, db, new Class[] {});
    }

    public WGAURLBuilder retrieveURLBuilder(HttpServletRequest request, WGDatabase db,
            Class[] extensionInterfaces) {

        WGAURLBuilder builder = null;
        if (request != null) {
            builder = (WGAURLBuilder) request.getAttribute(WGACore.ATTRIB_URLBUILDER);
        }

        // Create a new builder
        if (builder == null) {

            // Look if the builder to use is predefined by DB attribute
            Object urlBuilderClassName = db.getAttribute(WGACore.DBATTRIB_URLBUILDER);
            if (urlBuilderClassName != null && urlBuilderClassName instanceof String) {
                try {
                    Class builderClazz = WGACore.getLibraryLoader().loadClass((String) urlBuilderClassName);
                    builder = (WGAURLBuilder) builderClazz.newInstance();
                } catch (Exception e) {
                    getLog().error("Unable to instatiate url builder '" + urlBuilderClassName
                            + "'. Using default url builder.", e);

                }
            }

            // Check the extension interfaces. If the builder does not satisfy them, remove it
            if (builder != null) {
                for (Class ifClass : extensionInterfaces) {
                    if (!ifClass.isAssignableFrom(builder.getClass())) {
                        builder = null;
                        break;
                    }
                }
            }

            // Default/Fallback URL builder
            if (builder == null || (request == null && !(builder instanceof RequestIndependentURLBuilder))) {
                DefaultURLBuilder defBuilder = new RequestIndependentDefaultURLBuilder();
                if (request != null && "true".equals(request.getParameter("forceClassicURL"))) {
                    defBuilder.setTitlePathAllowed(false);
                }
                builder = defBuilder;
            }

            // Register new builder with current request
            if (request != null) {
                request.setAttribute(WGACore.ATTRIB_URLBUILDER, builder);
            }

        }

        // Initialize the new builder
        if (request == null) {
            ((RequestIndependentURLBuilder) builder).newIndependentInstance(this);
        } else {
            builder.newRequest(this, request);
        }

        return builder;
    }

    public List<HTMLHeadInclusion> getHtmlHeadInclusions() {
        return _htmlHeadInclusions;
    }

    public void setDefaultAnalyzer(Analyzer defaultAnalyzer) {
        this.defaultAnalyzer = defaultAnalyzer;
    }

    public String getClassLocation(String className) {
        return WGUtils.which(className, getLibraryLoader());
    }

    public boolean isRunSingleNodeFunctionalities() {
        ClusterConfiguration clusterConfig = getWgaConfiguration().getClusterConfiguration();
        if (clusterConfig != null && clusterConfig.isEnabled()) {
            return clusterConfig.isDefaultMasterNode();
        }
        return true;
    }

    public EventManager getEventManager() {
        return _eventManager;
    }

    public boolean testPasswordAgainstHash(String pwd, HashedPassword hashedPwd) throws HashingException {
        return hashedPwd.check(pwd, getModuleRegistry());
    }

    public boolean testPasswordAgainstHash(String pwd, String storedHash) throws HashingException {
        return testPasswordAgainstHash(pwd, new HashedPassword(storedHash));
    }

    public WGAConfigurationOptionReader getServicesServerOptionReader() {
        return _servicesServerOptionReader;
    }

    public JMX getJmx() {
        return _jmx;
    }

    protected long getConfigFileLastModified() {
        return configFileLastModified;
    }

    public XStream getDefaultSerializer() {
        return _defaultSerializer;
    }

    public Map<String, String> getContentdbUuidsToKeys() {
        return _contentdbUuidsToKeys;
    }

    public AbstractWGAHttpSessionManager getHttpSessionManager() {
        return _httpSessionManager;
    }

    private void updateFileAnnotators(WGDatabase db) throws WGException {

        if (!db.hasFeature(WGDatabase.FEATURE_FULLCONTENTFEATURES)) {
            return;
        }

        // Fetch definitions of configured or all annotators
        List<String> configuredAnnotators = (List<String>) WGA.get(WGACore.this).app(db)
                .getPublisherOption(DBATTRIB_FILE_ANNOTATORS);
        List<ModuleDefinition> annotatorDefs = new ArrayList<ModuleDefinition>();
        if (configuredAnnotators != null) {
            for (String annotatorName : configuredAnnotators) {
                ModuleDefinition annotatorDef = getModuleRegistry()
                        .getModuleDefinition(FileAnnotatorModuleType.class, annotatorName);
                if (annotatorDef != null) {
                    annotatorDefs.add(annotatorDef);
                } else {
                    getLog().warn("Unknown file annotator '" + configuredAnnotators + "' configured on app '"
                            + db.getDbReference() + "'");
                }
            }
        } else {
            annotatorDefs.addAll(getModuleRegistry().getModulesForType(FileAnnotatorModuleType.class).values());
        }

        // Instantiate annotators
        List<WGFileAnnotator> annotators = new ArrayList<WGFileAnnotator>();
        for (ModuleDefinition annotatorDef : annotatorDefs) {
            try {
                WGFileAnnotator annotator = (WGFileAnnotator) getModuleRegistry().instantiate(annotatorDef);
                annotators.add(annotator);
            } catch (Throwable e) {
                getLog().error(
                        "Exception instantiating file annotator " + annotatorDef.getImplementationClass().getName(),
                        e);
            }
        }

        // Set new annotators
        db.setFileAnnotators(annotators);
    }

    public AppLogAppender getTransientLogAppender() {
        return transientLogAppender;
    }

    public String getSystemLabel(String systemBundleName, String labelKey, HttpServletRequest servletRequest) {

        String label = null;
        PropertyResourceBundle bundle = null;

        List<Locale> locales = new ArrayList<Locale>();
        if (servletRequest != null) {
            locales.addAll(WGUtils.extractEntryList(servletRequest.getLocales()));
        }
        locales.add(Locale.getDefault());
        locales.add(Locale.ENGLISH);

        for (Locale locale : locales) {
            try {
                bundle = (PropertyResourceBundle) ResourceBundle.getBundle(
                        WGACore.SYSTEMLABEL_BASE_PATH + systemBundleName, locale, this.getClass().getClassLoader());
                label = bundle.getString(labelKey);
                if (label != null) {
                    return label;
                }
            } catch (java.util.MissingResourceException e) {
            }
        }

        return null;
    }

    public VirtualLinkTarget resolveVirtualLink(WGA wga, final WGContent content) throws WGAPIException {
        VirtualLinkTarget target = null;
        try {
            VirtualLinkResolver resolver = null;
            ModuleDefinition modDef = getModuleRegistry()
                    .getModuleDefinitionByKey(VirtualLinkResolverModuleType.class, content.getVirtualLinkType());
            if (modDef != null) {
                resolver = (VirtualLinkResolver) getModuleRegistry().instantiate(modDef);
            }

            // Fallback: External URL
            else {
                resolver = new ExternalVirtualLinkResolver();
            }

            target = resolver.resolve(wga, content);

        } catch (Exception e) {
            wga.server().getLog().error("Exception resolving virtual link on content " + content.getContentKey(),
                    e);
        }

        if (target == null) {
            ProblemOccasion occ = new ProblemOccasion() {

                @Override
                public boolean isClearedAutomatically() {
                    return false;
                }

                @Override
                public Class<? extends ProblemType> getDefaultType() {
                    return AdministrativeProblemType.class;
                }

                @Override
                public ProblemScope getDefaultScope() {
                    return new DatabaseScope(content.getDatabase().getDbReference());
                }

                @Override
                public Class<?> getDefaultRefClass() {
                    return WGACore.class;
                }
            };
            getProblemRegistry().addProblem(Problem.create(occ,
                    "contentProblem.invalidVirtualLink#" + content.getContentKey(), ProblemSeverity.LOW,
                    Problem.var("title", content.getTitle()).var("key", content.getContentKey().toString())));
        }

        return target;
    }

    public String getStartPageURL() {
        String url = WGABrand.get("startpage.url");
        if (url == null) {
            url = "/plugin-management/homepage:main";
        }
        return url;

    }

    public ScopeObjectRegistry getScopeObjectRegistry() {
        return _serverScopeObjectRegistry;
    }

    @Override
    public ClassLoader getClassLoader() {
        return getLibraryLoader();
    }

    public boolean isClusteredDatabase(WGDatabase db) {

        if (getWgaConfiguration().getClusterConfiguration() == null
                || getWgaConfiguration().getClusterConfiguration().isEnabled() == false) {
            return false;
        }

        if ("false".equals(db.getCreationOptions().get(WGDatabase.COPTION_CLUSTERED))) {
            return false;
        }

        return true;

    }

}