Package de.innovationgate.wgpublisher

Source Code of de.innovationgate.wgpublisher.WGACore$AsyncMailSender

/*******************************************************************************
* 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.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
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.FileReader;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.LineNumberReader;
import java.io.PrintWriter;
import java.io.Serializable;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
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.text.DateFormat;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.prefs.Preferences;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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.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.management.ObjectName;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
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.parsers.ParserConfigurationException;

import org.apache.commons.collections.map.LinkedMap;
import org.apache.commons.jxpath.CompiledExpression;
import org.apache.commons.jxpath.JXPathContext;
import org.apache.commons.collections.Buffer;
import org.apache.commons.collections.BufferUtils;
import org.apache.commons.collections.buffer.CircularFifoBuffer;
import org.apache.commons.vfs.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.dom4j.Document;
import org.dom4j.DocumentException;
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 de.innovationgate.monitoring.JmxManager;
import de.innovationgate.utils.Base64;
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.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.net.IPv4Address;
import de.innovationgate.utils.net.IPv4Restriction;
import de.innovationgate.webgate.api.MimetypeDeterminationService;
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.WGDatabaseConnectListener;
import de.innovationgate.webgate.api.WGDatabaseCore;
import de.innovationgate.webgate.api.WGDatabaseEvent;
import de.innovationgate.webgate.api.WGDesignProvider;
import de.innovationgate.webgate.api.WGException;
import de.innovationgate.webgate.api.WGFactory;
import de.innovationgate.webgate.api.WGHierarchicalDatabase;
import de.innovationgate.webgate.api.WGHierarchicalDatabaseCoreListener;
import de.innovationgate.webgate.api.WGIllegalArgumentException;
import de.innovationgate.webgate.api.WGIllegalStateException;
import de.innovationgate.webgate.api.WGLanguage;
import de.innovationgate.webgate.api.WGSystemException;
import de.innovationgate.webgate.api.WGUnavailableException;
import de.innovationgate.webgate.api.WGUserProfile;
import de.innovationgate.webgate.api.WGDatabase.ConnectAction;
import de.innovationgate.webgate.api.auth.AuthModuleFactory;
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.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.AuthenticationSource;
import de.innovationgate.wga.config.ConfigBean;
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.BrowsingSecurity;
import de.innovationgate.wga.model.ValidationError;
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.ModuleType;
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.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.FilterConfigModuleType;
import de.innovationgate.wga.modules.types.HTMLHeadInclusionModuleType;
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.WGAServerOptionsModuleType;
import de.innovationgate.wga.modules.types.WebTMLElementModuleType;
import de.innovationgate.wga.modules.types.WebTMLEncoderModuleType;
import de.innovationgate.wga.modules.types.WorkflowEngineModuleType;
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.WebTMLCache;
import de.innovationgate.wgpublisher.design.WGADesignManager;
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.TMLScriptGlobal;
import de.innovationgate.wgpublisher.hdb.HDBModel;
import de.innovationgate.wgpublisher.labels.WGAResourceBundleManager;
import de.innovationgate.wgpublisher.lang.DynamicLanguageBehaviour;
import de.innovationgate.wgpublisher.lang.LanguageBehaviour;
import de.innovationgate.wgpublisher.lang.OnlyDefaultLanguageBehaviour;
import de.innovationgate.wgpublisher.lang.StaticLanguageBehaviour;
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.lucene.analysis.StandardAnalyzer;
import de.innovationgate.wgpublisher.modules.poptions.ContentDatabasePublisherOptionsCollector;
import de.innovationgate.wgpublisher.modules.poptions.ContentStorePublisherOptionsCollector;
import de.innovationgate.wgpublisher.monitoring.WGAInformation;
import de.innovationgate.wgpublisher.plugins.InvalidPluginException;
import de.innovationgate.wgpublisher.plugins.PluginException;
import de.innovationgate.wgpublisher.plugins.WGAPlugin;
import de.innovationgate.wgpublisher.plugins.WGAPluginSet;
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.Scheduler;
import de.innovationgate.wgpublisher.scheduler.ScriptTask;
import de.innovationgate.wgpublisher.scheduler.Task;
import de.innovationgate.wgpublisher.shares.ShareDefinition;
import de.innovationgate.wgpublisher.shares.ShareInitException;
import de.innovationgate.wgpublisher.shares.ShareProperties;
import de.innovationgate.wgpublisher.url.DefaultURLBuilder;
import de.innovationgate.wgpublisher.url.TitlePathManager;
import de.innovationgate.wgpublisher.url.WGAURLBuilder;
import de.innovationgate.wgpublisher.webtml.utils.CRLFEncoder;
import de.innovationgate.wgpublisher.webtml.utils.EncodingFormatter;
import de.innovationgate.wgpublisher.webtml.utils.HTMLHeadInclusion;
import de.innovationgate.wgpublisher.webtml.utils.JavaScriptEncodingFormatter;
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.TMLScriptHDBListenerFactory;
import de.innovationgate.wgpublisher.webtml.utils.URLEncodingFormatter;
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 ServletContextListener, WGDatabaseConnectListener {

    public static final String EXTERNAL_PERSDBS_FOLDER = "#persdbs";
   
    /**
     * A reader for WGA configuration options, that always takes the current WGA configuration and caches once read option values
     */
    public class WGAConfigurationOptionReader {
       

        private CompiledExpression _jxPath;
        private Class<? extends ModuleType> _typeClass;
        private Class<?> _implClass;
   
        protected WGAConfigurationOptionReader(String xpath, Class<? extends ModuleType> typeClass, Class<?> implClass) {
            _jxPath = JXPathContext.compile(xpath);
            _typeClass = typeClass;
            _implClass = implClass;
           
            Object target = _jxPath.getValue(JXPathContext.newContext(getWgaConfiguration()));
            if (!(target instanceof Map)) {
                throw new IllegalArgumentException("Xpath " + xpath + " does not point to an options map");
            }
   
        }
       
        public Object readOptionValueOrDefault(String optionName) {
            Map<String,String> options = (Map<String,String>) _jxPath.getValue(JXPathContext.newContext(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(String xpath, Class<? extends ModuleType> typeClass, Class<?> implClass) {
        return new WGAConfigurationOptionReader(xpath, typeClass, implClass);
    }
   
    public class WGAMimetypeDeterminationService implements MimetypeDeterminationService {

        public String determineByFilename(String fileName) {
            return getServletContext().getMimeType(fileName);
        }
       
    }

    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(getConfigFilePath());
            }
            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_JAVASCRIPT = "javascript";
   
    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 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";
   
   
   
    /*
     * Server options
     */
    public static final String SERVEROPTION_SERVER_SCALINGTHRESHOLD = "Server.ScalingThreshold";


   
    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_ISHDB = "isHDB";
   
    //public static final String DBATTRIB_HDBINIT = "HDBInit";
   
    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 class DevPluginsMonitoringTask extends TimerTask {
       
        private File _dir;
        private long _lastChanged;

        public DevPluginsMonitoringTask(File dir) {
            _dir = dir;
            _lastChanged = getLatestChange();
        }

        private long getLatestChange() {
           
            long latestChange = Long.MIN_VALUE;
            File[] files = _dir.listFiles();
            for (File f : files) {
                long fileLastMod = f.lastModified();
                latestChange = (fileLastMod > latestChange ? fileLastMod : latestChange);
                if (f.isDirectory()) {
                    File[] childFiles = f.listFiles();
                    for (File f2 : childFiles) {
                        long fileLastMod2 = f2.lastModified();
                        latestChange = (fileLastMod2 > latestChange ? fileLastMod2 : latestChange);   
                    }
                }
            }
            return latestChange;
           
        }

        @Override
        public void run() {
           
            try {
               
                Thread.currentThread().setName("WGA Developer Plugins Monitoring Task");
               
                synchronized (WGACore.this) {
                    boolean anythingRemoved = uninstallRemovedDefaultPlugins();
                   
                    long newLastChanged = getLatestChange();
                    if (anythingRemoved || newLastChanged > _lastChanged) {
                        _lastChanged = newLastChanged;
                        updatePlugins();
                    }
                }
               
            }
            catch (Throwable e) {
                getLog().error("Exception executing developer plugins monitoring task", e);
            }
           
           
           
        }
       
    }
   
    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 final class ApplogPage {
       
        private boolean pageExisting = false;
        private boolean endReached = false;
        private int endIndex = -1;
        private List<ApplogMessage> _messages = null;
       
        public ApplogPage(boolean pageReached, boolean endReached, int endIndex, List<ApplogMessage> messages) {
            super();
            this.pageExisting = pageReached;
            this.endIndex = endIndex;
            this.endReached = endReached;
            _messages = messages;
        }
       
        public ApplogPage(boolean pageReached, boolean endReached, int endIndex) {
            this(pageReached, endReached, endIndex, null);
        }
       
       
        public boolean isPageExisting() {
            return pageExisting;
        }
        public int getEndIndex() {
            return endIndex;
        }
        public boolean isEndReached() {
            return endReached;
        }

        public List<ApplogMessage> getMessages() {
            return _messages;
        }
    }
   
    public static final class ApplogMessage {
       
        public static final DecimalFormat LINENUMBER_FORMAT = new DecimalFormat("000000");
        public static final Pattern MESSAGE_PATTERN = Pattern.compile("(\\d+.\\d+.\\d+ \\d+:\\d+:\\d+) (\\w+) (.+)");
        public static final DateFormat MESSAGE_DATEFORMAT = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss");

        private Date _time;
        private LogLevel _level;
        private String _mainMessage;
        private StringBuffer _details = null;
        private int _line;
       
        public static ApplogMessage parseLine(String line, int lineNumber) {
           
            Matcher messageMatcher = ApplogMessage.MESSAGE_PATTERN.matcher(line);
            if (messageMatcher.matches()) {
                return new ApplogMessage(messageMatcher, lineNumber);
            }
            else {
                return null;
            }
           
        }
       
        public ApplogMessage(Date time, LogLevel level, String mainMessage, int line) {
            super();
            _time = time;
            _level = level;
            _mainMessage = mainMessage;
            _line = line;
        }
       
        public ApplogMessage(Matcher messageMatcher, int line) {
           
            String dateStr = messageMatcher.group(1);
            try {
                _time = MESSAGE_DATEFORMAT.parse(dateStr);
            }
            catch (ParseException e) {
                // Shouldn't happen. If it does we display bogus date
                _time = new Date(Long.MIN_VALUE);
            }
           
            String logLevelStr = messageMatcher.group(2);
            _level = LogLevel.getLevel(logLevelStr);
           
            _mainMessage = messageMatcher.group(3);
            _line = line;
        }
       
        public void addDetailsLine(String line) {
            if (_details == null) {
                _details = new StringBuffer();
            }
           
            _details.append(line).append("\n");
        }
       
        public Date getTime() {
            return _time;
        }
        public LogLevel getLevel() {
            return _level;
        }
        public String getMainMessage() {
            return _mainMessage;
        }
        public String getDetails() {
            if (_details != null) {
                return _details.toString();
            }
            else {
                return null;
            }
        }

        public int getLine() {
            return _line;
        }

        @Override
        public String toString() {
          
            StringBuffer str = new StringBuffer();
            str.append(LINENUMBER_FORMAT.format(_line));
            str.append(" ");
            str.append(MESSAGE_DATEFORMAT.format(_time));
            str.append(" ");
            str.append(_level.toString());
            str.append(" ");
            str.append(_mainMessage);
           
            String details = getDetails();
            if (details != null) {
                str.append("\n").append(details);
            }
           
            return str.toString();
           
        }
       
    }
   
    /**
     * Performs cleanup operations
     */
    private final class CleanupTask extends TimerTask {
        public void run() {
           
            try {
                Thread.currentThread().setName("WGA Core Cleanup Task");
           
            // Cleanup transient scheduler jobs
            getScheduler().clearTransientJobs();
           
            }
            catch (Throwable e) {
                getLog().error("Exception running cleanup task", e);
            }

        }
    }

    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 class DomainConfiguration {
      public static final int LOGINMODE_MASTER = 1;
      public static final int LOGINMODE_USER = 2;
    private Domain _config;
    private AuthenticationModule _authModule;

        /**
         * @return Returns the name.
         */
        public String getName() {
            return _config.getName();
        }

        public DomainConfiguration(String name) {
            _config = new Domain(name);
            _config.setName(name);
        }

        public DomainConfiguration(Domain config) {
          _config = config;
        }

        protected void init() {
          if (_config != null) {
              // Authentication
              try {
                  AuthModuleFactory authFactory = WGFactory.getAuthModuleFactory();
                  AuthenticationSource authSource = _config.getAuthenticationSource();
                  if (authSource != null) {
                    _authModule = authFactory.getAuthModule(authSource.getImplClassName(), authSource.getOptions(), null);
                  }
              }
              catch (de.innovationgate.webgate.api.auth.ConfigurationException e) {
                  getLog().error("Authentication module of domain '" + getName() + "' is misconfigured and inactive", e);
              }
          }
        }

        /**
         * @return Returns the authModule.
         */
        public AuthenticationModule getAuthModule() {
            return _authModule;
        }
       
        public AuthenticationSource getAuthenticationSource() {
          if (_config != null) {
            return _config.getAuthenticationSource();
          } else {
            return null;
          }
        }

    public String getDefaultManager() {
      return _config.getDefaultManager();
    }


    public int getMaximumLoginAttempts() {
      return _config.getMaximumLoginAttempts();
    }


    public PersonalisationDatabase getPersonalisation() {
      if (_config != null) {
        return _config.getPersonalisation();
      } else {
        return null;
      }
    }
   
    public int getLoginMode() {
            return LOGINMODE_USER;
        }

        @Override
    public String toString() {
      if (_config != null) {
        return _config.toString();
      } else {         
        return super.toString();
      }
    }
       
        public void destroy() {
           if (_authModule != null) {
               _authModule.destroy();
               _authModule = null;
           }
        }
       
        public String getUID() {
            return _config.getUid();
        }
    }
   
    public class ResourceConfiguration {
       
        private String _faviconPath = null;
        private String _defaultDatabase = null;
        private int _staticExpiration = 10;
       
        public ResourceConfiguration(Element defaultdb) {
           
            setDefaultDatabase(defaultdb.attributeValue("key", null));
            setFaviconPath(defaultdb.attributeValue("favicon", ""));
           
            try {
                setStaticExpiration(Integer.parseInt(defaultdb.attributeValue("staticexpiration", "10000")));
            }
            catch (NumberFormatException e1) {
                getLog().error("Unable to parse Cache Expiration for static resources: " + defaultdb.attributeValue("staticexpiration", "10000"));
            }
           
            String dataCacheStr = defaultdb.attributeValue("datacache", "10000");
            try {
                int dataCache = Integer.parseInt(dataCacheStr);
                if (dataCache != WGFactory.getInstance().getCacheCapacity()) {
                    getLog().info("WGAPI Data Cache Capacity is set to " + dataCache + " entries" + (WGFactory.getInstance().isCacheInitialized() ? " (ineffective until a data cache reset)" : ""));
                    WGFactory.getInstance().setCacheCapacity(dataCache);
                }
            }
            catch (NumberFormatException e) {
                getLog().error("Unable to parse WGAPI Data Cache Size as number: " + dataCacheStr);
            }
           
        }
       
        public String getFaviconPath() {
            return _faviconPath;
        }
        public void setFaviconPath(String faviconPath) {
           
            if (faviconPath != null && faviconPath.trim().equals("")) {
                faviconPath = null;
            }
           
            _faviconPath = faviconPath;
           
        }
        public String getDefaultDatabase() {
            return _defaultDatabase;
        }
        public void setDefaultDatabase(String defaultDatabase) {

            if (defaultDatabase != null && defaultDatabase.trim().equals("")) {
                defaultDatabase = null;
            }
           
            _defaultDatabase = defaultDatabase;
        }
        public int getStaticExpiration() {
            return _staticExpiration;
        }
        public void setStaticExpiration(int staticExpiration) {
            _staticExpiration = staticExpiration;
        }
       
    }

    public static final String ATTRIB_TMLDEBUG_TRACE_RESULTS = "tmlDebugTraceResults";



    public static final String ATTRIB_CURRENTTML = "currenttml";

    public static final String ATTRIB_AJAXINFO = "ajaxInfo";



    private Scheduler _scheduler;

    private BruteForceLoginBlocker bruteForceLoginBlocker = new BruteForceLoginBlocker(this);

    private List syncMappings;

    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_INCLUDELEVEL = "$IncludeLevel";

    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";

    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";

    public class LicenseBindingKey implements Comparable {

        private String _domain;

        private String _name;

        private String _type;

        public LicenseBindingKey(String domain, String name, String type) {
            _domain = domain.toLowerCase();
            _name = name.toLowerCase();
            _type = type;
        }

        public LicenseBindingKey(String complete) {

            List elements = WGUtils.deserializeCollection(complete, "//");
            _domain = (String) elements.get(0);
            _name = (String) elements.get(1);
            _type = (String) elements.get(2);

        }

        /*
         * (Kein Javadoc)
         *
         * @see java.lang.Object#toString()
         */
        public String toString() {
            return _domain + "//" + _name + "//" + _type;
        }

        public String getDomain() {
            return _domain;
        }

        public String getName() {
            return _name;
        }

        public String getType() {
            return _type;
        }

        /*
         * (Kein Javadoc)
         *
         * @see java.lang.Comparable#compareTo(java.lang.Object)
         */
        public int compareTo(Object o) {
            return toString().compareTo(o.toString());
        }

        /*
         * (Kein Javadoc)
         *
         * @see java.lang.Object#equals(java.lang.Object)
         */
        public boolean equals(Object obj) {
            return toString().equals(obj.toString());
        }

        /*
         * (Kein Javadoc)
         *
         * @see java.lang.Object#hashCode()
         */
        public int hashCode() {
            return toString().hashCode();
        }

    }

    /**
     * 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 = this.getSessionLogins(session).get(db.getAttribute(WGACore.DBATTRIB_DOMAIN));
            return (loginInfo == null);
        }

    }

    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 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 ApplogPage writeApplogContent(int offset, int size, Writer out, LogLevel loglevel, boolean lineNumbers) {
       
        DecimalFormat lineNumberFormat = new DecimalFormat("000000");
        Iterator logFiles = transientLogAppender.getActiveFiles().iterator();
       
        if (offset < 1) {
            return new ApplogPage(false, false, -1);
        }
       
        int totalLinesCount = 0;
        boolean firstLine = true;
        boolean pageReached = false;
        boolean endReached = true;
       
        filesloop:
        while (logFiles.hasNext()) {
           
            ApplogAppender.AppenderFile appenderFile = (ApplogAppender.AppenderFile) logFiles.next();
           
            // If the line count of this logfile is known, and it is not the last one (which still might grow)
            // it might be skipped when we need a line beyond it
            if (logFiles.hasNext() && appenderFile.getLinesCount() != -1) {
                if (totalLinesCount + appenderFile.getLinesCount() < offset) {
                    totalLinesCount += appenderFile.getLinesCount();
                    continue;
                }
            }

            synchronized (appenderFile.getFile()) {
   
               LineNumberReader reader = null;
                try {
                    reader = new LineNumberReader(new FileReader(appenderFile.getFile()));
                    LogLevel level = LogLevel.LEVEL_INFO;
                    String line;
                   
                    // Skip lines that are below the offset
                    while (offset > (totalLinesCount + 1)) {
                        line = reader.readLine();
                        if (line == null) {
                            appenderFile.setLinesCount(reader.getLineNumber());
                            continue filesloop;
                        }
                        totalLinesCount++;
                        level = LogLevel.isolateLogLevel(line, 20, level);
                    }
   
                    int toLine = offset + size - 1;
                   
                   
                    // Read lines that are in the targeted region
                    while (totalLinesCount < toLine) {
                        line = reader.readLine();
                        if (line == null) {
                            appenderFile.setLinesCount(reader.getLineNumber());
                            continue filesloop;
                        }
                       
                        totalLinesCount++;
                        level = LogLevel.isolateLogLevel(line, 20, level);
                       
                        // Filter level
                        if (level.isHigherOrEqual(loglevel)) {
                            pageReached = true;
                           
                            // Add line break only if not on first line
                            if (firstLine) {
                                firstLine = false;
                            }
                            else {
                                out.write("\n");
                            }
                           
                            if (lineNumbers) {
                                out.write(lineNumberFormat.format(totalLinesCount));
                                out.write(" ");
                            }
                           
                            out.write(line);
                        }
                        else {
                            toLine++;
                        }
                    }
                    endReached = false;
                }
                catch (FileNotFoundException e) {
                    getLog().error("Error reading application log", e);
                    return new ApplogPage(false, false, totalLinesCount);
                }
                catch (IOException e) {
                    getLog().error("Error reading application log", e);
                    return new ApplogPage(false, false, totalLinesCount);
                }
                finally {
                    try {
                        if (reader != null) {
                            reader.close();
                        }
                    }
                    catch (IOException e) {
                        getLog().error("Error closing application log reader", e);
                    }
                }
            }
       
        }
       
        return new ApplogPage(pageReached, endReached, totalLinesCount);
           
    }



    public int getApplogLines() {
        return getApplogContent(Integer.MAX_VALUE, 1, LogLevel.LEVEL_ALL).getEndIndex();
    }

    public ApplogPage getApplogContent(int offset, int size, LogLevel loglevel) {
       
        DecimalFormat lineNumberFormat = new DecimalFormat("000000");
        Iterator logFiles = transientLogAppender.getActiveFiles().iterator();
       
        if (offset < 1) {
            return new ApplogPage(false, false, -1);
        }
       
        int totalLinesCount = 0;
        boolean firstLine = true;
        boolean pageReached = false;
        boolean endReached = true;
       
        List<ApplogMessage> messages = new ArrayList<ApplogMessage>();
        ApplogMessage currentMessage = null;
       
        filesloop:
        while (logFiles.hasNext()) {
           
            endReached = false;
            ApplogAppender.AppenderFile appenderFile = (ApplogAppender.AppenderFile) logFiles.next();
           
            // If the line count of this logfile is known, and it is not the last one (which still might grow)
            // it might be skipped when we need a line beyond it
            if (logFiles.hasNext() && appenderFile.getLinesCount() != -1) {
                if (totalLinesCount + appenderFile.getLinesCount() < offset) {
                    totalLinesCount += appenderFile.getLinesCount();
                    continue;
                }
            }

            synchronized (appenderFile.getFile()) {
   
               LineNumberReader reader = null;
                try {
                    reader = new LineNumberReader(new FileReader(appenderFile.getFile()));
                    String line;
                   
                    // Skip lines that are below the offset
                    while (offset > (totalLinesCount + 1)) {
                        line = reader.readLine();
                        if (line == null) {
                            appenderFile.setLinesCount(reader.getLineNumber());
                            endReached = true;
                            continue filesloop;
                        }
                        totalLinesCount++;
                    }
   
                    // Read lines that are in the targeted region
                    while (true) {
                       
                        line = reader.readLine();
                        if (line == null) {
                            appenderFile.setLinesCount(reader.getLineNumber());
                            endReached = true;
                            continue filesloop;
                        }
                       
                        totalLinesCount++;
                       
                        ApplogMessage newMessage = ApplogMessage.parseLine(line, totalLinesCount);
                        if (newMessage != null) {
                           
                            // Add previous message to list
                            if (currentMessage != null) {
                                if (currentMessage.getLevel().isHigherOrEqual(loglevel)) {
                                    pageReached = true;
                                    messages.add(currentMessage);
                                    currentMessage = null;
                                   
                                    // The message size is reached we go back one line and exit the loop
                                    if (messages.size() == size) {
                                        totalLinesCount--;
                                        break filesloop;
                                    }
                                }
                            }
                           
                            currentMessage = newMessage;
                        }
                        else {
                            if (currentMessage != null) {
                                currentMessage.addDetailsLine(line);
                            }
                            // The offset does not start with a regular message. We ignore these lines
                            else {
                                continue;
                            }
                        }
                    }
                }
                catch (FileNotFoundException e) {
                    getLog().error("Error reading application log", e);
                    return new ApplogPage(false, false, totalLinesCount);
                }
                catch (IOException e) {
                    getLog().error("Error reading application log", e);
                    return new ApplogPage(false, false, totalLinesCount);
                }
                finally {
                    try {
                        if (reader != null) {
                            reader.close();
                        }
                    }
                    catch (IOException e) {
                        getLog().error("Error closing application log reader", e);
                    }
                }
            }
       
        }
       
        if (currentMessage != null && currentMessage.getLevel().isHigherOrEqual(loglevel)) {
            messages.add(currentMessage);
        }
       
        return new ApplogPage(pageReached, endReached, totalLinesCount, messages);
    }

    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 HashMap<Object, DBLoginInfo> getSessionLogins(javax.servlet.http.HttpSession session) {

        HashMap<Object, DBLoginInfo> logins = (HashMap<Object, DBLoginInfo>) session.getAttribute(WGPDispatcher.SESSION_LOGINS);
        if (logins == null) {
            logins = new HashMap<Object, DBLoginInfo>();
            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, useMaster, null);
    }

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

        // If already open, just return
        if (db.isSessionOpen()) {
            return db;
        }
       
        // If db is not ready throw an exception
        if (!db.isReady()) {
            throw new WGUnavailableException(db);
        }

        // Force master login
        if (useMaster) {
            db.openSession(null, null);
            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 db '" + db.getDbReference() + "'.");
        }
       
        // Check if a regular login is forced by some filter
        Boolean forceRegular = (request != null ? (Boolean) request.getAttribute(ATTRIB_FORCEREGULARLOGIN) : null);
        if (forceRegular == null) {
            forceRegular = Boolean.FALSE;
        }

        // Certificate based login
        if (!forceRegular.booleanValue() && db.certAuthEnabled()) {
            return openContentDBCertAuth(db, request);
        }
       
        // Requestinfo based login
        else if (!forceRegular.booleanValue() && db.getAuthenticationModule() != null && db.getAuthenticationModule() instanceof RequestBasedAuthenticationModule) {
            return openContentDBRequestBased(db, request);
        }
       
        else {

            // do standard login process

            DomainConfiguration domainConfig = getDomainConfigForDatabase(db);

            // If no session available, log in by hint or anonymous
            HttpSession session = (request != null ? request.getSession() : null);
            if (session == null) {
                if (hintDB != null && hintDB.isSessionOpen()) {
                    DomainConfiguration hintDomain = getDomainConfigForDatabase(hintDB);
                    if (hintDomain.getLoginMode() == domainConfig.getLoginMode()) {
                        if (hintDB.getSessionContext().isMasterSession()) {
                            db.openSession();
                        }
                        else {
                            db.openSession(hintDB.getSessionContext().getUser(), hintDB.getSessionContext().getPassword());
                        }
                        if (db.isSessionOpen()) {
                            return prepareDB(db, request);
                        }
                    }
                }

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

            // Look if domain is defined

            // If login mode MASTER, log in by master login
            if (domainConfig.getLoginMode() == DomainConfiguration.LOGINMODE_MASTER) {
                db.openSession(null, null);
                return prepareDB(db, request);
            }

            // Try to login by (previously stored) domain-specific login
            DBLoginInfo loginInfo = this.getSessionLogins(session).get(domainConfig.getName());
            if (loginInfo != null) {
                int accessLevel = db.openSession(loginInfo.getUserName(), loginInfo.getCredentials());
                if (accessLevel > WGDatabase.ACCESSLEVEL_NOTLOGGEDIN) {
                    return prepareDB(db, request);
                }
                else {
                    if (domainConfig.getAuthModule() != null) {
                        getLog().warn(
                                "User '" + loginInfo.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 '" + loginInfo.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);
                }
            }

            // Attempts to use other login information
            // Try to login via session token
            if (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());
                            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);
                            }
                        }
                    }
                }
            }

            // Try to login by default (HTTP) Login
            loginInfo = (DBLoginInfo) session.getAttribute(WGPRequestPath.SESSION_HTTPLOGIN);
            if (loginInfo != null) {
                db.openSession(loginInfo.getUserName(), loginInfo.getCredentials());
                if (db.isSessionOpen()) {
                    this.getSessionLogins(session).put(domainConfig.getName(), loginInfo);
                    return prepareDB(db, request);
                }
            }

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

    /**
     * opens a content db based upon request.getRemoteUser and request.getUserPrincipal()
     * if request.getRemoteUser is '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) throws WGAPIException, ClientAccessException {
      if (request == null) {
            return prepareDB(db, request);
        }          
       
        String user = request.getRemoteUser();
        if (user == null) {
          user = WGDatabase.UNKNOWN_REMOTE_USER;         
        }
       
        Principal credentials = request.getUserPrincipal();
       
        db.openSession(user, credentials);
       
       
        if (db.isSessionOpen()) {
            // create login info
            DBLoginInfo loginInfo = new DBLoginInfo(db.getSessionContext().getUser(), credentials);
            this.getSessionLogins(request.getSession()).put(db.getAttribute(DBATTRIB_DOMAIN), loginInfo);                   
        }
       
        return prepareDB(db, request);
  }

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

        if (request == null) {
            return prepareDB(db, request);
        }

        // 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);
        if (db.isSessionOpen()) {
            // create login info
            //  This prevents domains from using mixed cert and regular auth bc. of credential type conflicts
            DBLoginInfo loginInfo = new DBLoginInfo(db.getSessionContext().getUser(), clientCert);
            this.getSessionLogins(request.getSession()).put(db.getAttribute(DBATTRIB_DOMAIN), loginInfo);
        }
        return prepareDB(db, request);
    }

    /**
     * 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 domain
     * @return true/false
     */
    public boolean certAuthEnabledForDomain(String domain) {
     
        DomainConfiguration domainCfg = getDomainConfig(domain);
        if (domainCfg == null) {
            return false;
        }
       
        if (domainCfg.getAuthenticationSource() instanceof CertAuthCapableAuthModule) {
            CertAuthCapableAuthModule certAuthModule = (CertAuthCapableAuthModule) domainCfg.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, javax.servlet.http.HttpSession session) throws WGAPIException {

        DomainConfiguration domainCfg = getDomainConfig(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);
        }
   

    }
   
    public class TMLScriptGlobalRegistry {
       
        private Map<String, TMLScriptGlobal> _tmlscriptGlobals = WGUtils.createSynchronizedMap();
       
        public boolean registerGlobal(TMLScriptGlobal scriptGlobal) {
            getLog().info("Registering TMLScript Global \"" + scriptGlobal.getName() + "\"");
            TMLScriptGlobal previousGlobal = _tmlscriptGlobals.get(scriptGlobal.getName());
            _tmlscriptGlobals.put(scriptGlobal.getName(), scriptGlobal);
            return true;
        }
       
        public boolean registerDBGlobal(TMLScriptGlobal scriptGlobal, WGDatabase db) {
            getLog().info("Registering TMLScript DB Global \"" + scriptGlobal.getName() + "\" for database " + db.getDbReference());
            Map<String, TMLScriptGlobal> globals = (Map<String, TMLScriptGlobal>) db.getAttribute(WGACore.DBATTRIB_DBGlOBALS);
            TMLScriptGlobal previousGlobal = globals.get(scriptGlobal.getName());
            globals.put(scriptGlobal.getName(), scriptGlobal);
            return true;
        }
       
        public TMLScriptGlobal getGlobal(String name,  WGDatabase db) {
           
            Map globals = (Map) db.getAttribute(WGACore.DBATTRIB_DBGlOBALS);
            TMLScriptGlobal global = (TMLScriptGlobal) globals.get(name);
            if (global != null) {
                return global;
            }
           
            return _tmlscriptGlobals.get(name);
        }
       
        public boolean isGlobalDefined(String name, WGDatabase db) {
           
            Map globals = (Map) db.getAttribute(WGACore.DBATTRIB_DBGlOBALS);
            boolean contained = globals.containsKey(name);
            if (contained) {
                return true;
            }
           
            return _tmlscriptGlobals.containsKey(name);
        }

       
    }

    // 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) {

            /*
             * if (db.isSessionOpen()) { db.closeSession(); }
             */

            // Deregister from wgacore objects
            this._systemContainerManager.removeDatabase(db);
            this.deployer.removeLayouts(db);
            this.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);
            }

        }

    }
   
    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";

 



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

        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) {
            log.error("Could not open database for key " + strKey.toLowerCase() + ": The server " + config.getDbServer() + " is unknown");
            return null;
        }
        
        // TODO this should be done in configuration validation not here
        if (strKey.equalsIgnoreCase("start") || strKey.equalsIgnoreCase("bi") || strKey.equalsIgnoreCase("static") || strKey.equalsIgnoreCase("statictml")) {
          String message = "Cannot map " + dbType + " to key \"" + strKey + "\" - This is a predefined key not available for content databases";
            log.error(message);
            dbConnectionFailures.put(strKey, message);
            return null;
        }
       
        // TODO this should be done in configuration validation not here       
        if (strKey.startsWith(PluginConfig.PLUGIN_DBKEY_PREFIX)) {
          String message = "Cannot map " + dbType + " to key \"" + strKey + "\" - The key prefix \"" + PluginConfig.PLUGIN_DBKEY_PREFIX + "\" is reserved for WGA plugins";
            log.error(message);
            dbConnectionFailures.put(strKey, message);
            return null;
        }

        // 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)) {
                String message = "Cannot map " + dbType + " to key \"" + strKey + "\" - Database implementation class \"" + strType + "\" is no WGDatabaseCore implementation.";
                return null;
            }
            typeClass = theClass;
        }
        catch (ClassNotFoundException e) {
          String message = "Cannot map " + dbType + " to key \"" + strKey + "\" - Database implementation class \"" + strType + "\" not available.";
            log.error(message);
            dbConnectionFailures.put(strKey, message);
            return null;
        }
        catch (NoClassDefFoundError e) {
          String message = "Cannot map " + dbType + " to key \"" + strKey + "\" - Database implementation class or dependent class for type \"" + strType + "\" not found: \"" + e.getMessage();
            log.error(message);
            dbConnectionFailures.put(strKey, message);
            return null;
        }
        catch (VerifyError e) {
          String message = "Cannot map " + dbType + "to key \"" + strKey + "\" - Verify error: " + e.getMessage();
            log.error(message);
            dbConnectionFailures.put(strKey, message);
            return null;
        }

        if (!isDBAllowed(typeClass, strKey)) {
          String message = "Cannot map " + dbType + " to key \"" + strKey + "\" - Your server license type is not allowed to use this database type";
            log.error(message);
            dbConnectionFailures.put(strKey, message);
            return null;
        }

        // 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 domain = getWgaConfiguration().getDomain(config.getDomain());
        DomainConfiguration domainConfig = getDomainConfig(domain);

        // Collect db options from global, server, database
        HashMap<String, String> dbOptions = new HashMap<String, String>();
        dbOptions.put(WGDatabase.COPTION_USERCACHELATENCY, String.valueOf(_wgaConfiguration.getUserCacheLatencyMinutes()));
        dbOptions.putAll(_globalDbOptions);
        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());
       
        // get the database object
        WGDatabase db = null;
        try {
            if (config.isLazyConnecting()) {
                db = server.prepareDatabase(typeClass, dbOptions);
            }
            else {
                db = server.openDatabase(typeClass, dbOptions);
            }
        }
        catch (Throwable e1) {
          String message = "Could not open database for key " + strKey.toLowerCase();
            log.error(message, e1);
            dbConnectionFailures.put(strKey, e1);
            return null;
        }

        if (db == null || (!config.isLazyConnecting() && !db.isSessionOpen())) {         
            log.error("Could not open database for key " + strKey.toLowerCase() + " - Check logged messages above for error details");
            dbConnectionFailures.put(strKey, "Could not open database for key " + strKey.toLowerCase() + " - Check wga log for details");
            return null;
        }

        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_NO_CONTENTSTORE) {
                    log.info("Database for " + strKey.toLowerCase() + " is a WGA Content Store of version " + db.getContentStoreVersion());
                }
            }
            catch (WGAPIException e) {
                log.error("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 {
                try {
                    db.determineDefaultLanguage();
                }
                catch (WGAPIException e) {
                    getLog().error("Exception determining default language for " + dbType + " " + db.getDbReference(), e);
                }
            }
        }
       
        // Set mandatory database attributes
        initializeDBAttributes(db, strKey, domainConfig.getName(), firstLevelDBOptions);
       
        // Inject domain authentication module
        if (domainConfig.getAuthModule() != null) {
            try {
                db.setAuthenticationModule(new DomainRedirectionAuthModule(this, domainConfig.getName()));
            }
            catch (WGIllegalArgumentException e) {
                String message = "Exception setting authentication module of " + dbType + " '" + strKey + "'";
                getLog().error(message, e);
                getLog().error("Cancelling connection of " + dbType + " '" + strKey + "' because the configured authentication could not be set");
                dbConnectionFailures.put(strKey, message);
                try {
                    db.close();
                }
                catch (Exception e2) {
                }
                return null;
            }
        }

        // Configure design provider, if neccessary
        if (config instanceof ContentStore) {
          ContentStore csConfig = (ContentStore) config;
          if (csConfig.getDesign() != null) {
            getDesignManager().applyDesign(db, csConfig)
          }
        }
       
        // 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 && domainConfig.getDefaultManager() != null) {
            try {
                getLog().info("Adding default manager '" + domainConfig.getDefaultManager() + "' to ACL of '" + strKey + "'");
                db.getACL().createUserEntry(domainConfig.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 (InvalidCSConfigVersionException e) {
            this.log.error("Unable to process system file container. The design of " + dbType + " '" + db.getDbReference() + "' was developed for a higher WGA version: " + e.getTargetVersion());
        }
        catch (Exception e) {
            this.log.error("Exception processing system file container for " + dbType + " '" + strKey.toLowerCase() + "'", e);
        }
               
        // Merge publisher options from global, server, design, database
        Map<String, String> publisherOptions = new HashMap<String, String>();
        publisherOptions.putAll(_globalPublisherOptions);
        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) && domainConfig.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);
            }
        }
       
        // 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;
    }



    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 DomainConfiguration getDomainConfig(Domain domain) {     
    return getDomainConfig(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, WGUtils.createSynchronizedMap());
        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) {
      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)) {
                String message = "Cannot connect personalisation database for domain \"" + domainConfig.toString() + "\" - Database implementation class \"" + strType + "\" is no WGDatabaseCore implementation.";
                return null;
            }
            typeClass = theClass;
        }
        catch (ClassNotFoundException e) {
            log.error("Cannot attach personalisation database to domain \"" + domainConfig.toString() + "\" - Database implementation class \"" + strType + "\" not available.");
            return null;
        }
        catch (NoClassDefFoundError e) {
            log.error("Cannot attach personalisation database to domain \"" + domainConfig.toString() + "\" - Database implementation class or dependent class for type \"" + strType + "\" not found: \""
                    + e.getMessage());
            return null;
        }
        catch (VerifyError e) {
            log.error("Cannot attach personalisation database to domain \"" + domainConfig.toString() + "\" - Verify error: " + e.getMessage());
            return null;
        }

        // Look if license allows it
        if (!isPersonalisationDBAllowed(typeClass, domainConfig.getUid())) {
            log.error("Cannot attach personalisation database to domain \"" + domainConfig.toString() + "\" - Your server license type is not allowed to use this database type");
            return null;
        }

        // Retrieve server
        WGDatabaseServer server = getDatabaseServers().get(config.getDbServer());
        if (server == null) {
            log.error("Could not open personalisation database for domain '" + domainConfig.toString() + "': The server " + config.getDbServer() + " is unknown");
            return null;
        }
       
        // Merge db options from global, server, database
        HashMap<String, String> dbOptions = new HashMap<String, String>();
        dbOptions.putAll(_globalDbOptions);
        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) {
            getLog().error("Could not connect to database: " + e.getMessage());
        }
        catch (ModuleDependencyException e) {
            getLog().error("Could not connect to database because of missing dependency: " + e.getMessage());
        }

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

        }

        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) {
                log.error("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;
    }

    public class ConfigUpdateTask extends TimerTask {

        private WGACore _core;

        public ConfigUpdateTask(WGACore core) {
            _core = core;
        }

        /**
         * @see java.lang.Runnable#run()
         */
        public void run() {

            Thread.currentThread().setName("WGA.MainTimer");

            try {
                synchronized(WGACore.this) {
                    if (_core.configFile.lastModified() > _core.configFileLastModified) {
                        _core.updateConfig();
                    }
                }
            }
            catch (Exception e) {
                Logger.getLogger("wga").error("Error updating configuration", e);
            }
        }

    }



    private ServletContext _context;

    protected int tmlBuffer = 8;

    protected String tmlHeader = "";

    private boolean profilingEnabled = false;

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


    /**
     *
     * @param deploy
     * @return set containing dbkeys which where initial connected in this
     *         configUpdate
     */
    public synchronized void updateContentDBs(boolean deploy) {
        // 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
            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);
   
                    isNewDB = true;
                    newConnectedDBKeys.add(dbKey);
                }
                catch (Throwable e) {
                    getLog().error("Exception connecting database " + dbKey, e);
                    if (this.contentdbs.containsKey(dbKey)) {
                        this.contentdbs.remove(dbKey);
                    }
                    continue;
                }
            }
            else {
                db = this.contentdbs.get(dbKey);
            }
            currentDBs.add(dbKey);

            // Update stored queries
            /*
            Element queryRoot = (Element) databaseElement.selectSingleNode("storedqueries");
            if (queryRoot != null) {
                updateStoredQueries(db, queryRoot);
            }*/

            // Update field mappings
            /*
            Element mappingRoot = (Element) databaseElement.selectSingleNode("fieldmappings");
            if (mappingRoot != null) {
               
            }*/
            updateFieldMappings(db, databaseConfig.getFieldMappings());
           
            // Update client restrictions
            if (databaseConfig.isClientRestrictionsEnabled()) {
              db.setAttribute(DBATTRIB_CLIENTRESTRICTIONS, IPv4Restriction.getRestrictions(databaseConfig.getClientRestrictions(), databaseConfig.getKey(), getLog()));
            } 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 follwing databases has 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() {
       
        // 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() + "'");
                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:");
                Iterator msgs = e.getDetailMessages().iterator();
                while (msgs.hasNext()) {
                    getLog().error("- " + msgs.next());
                }
            }
            catch (Exception e) {
                getLog().error("Exception initializing content share '" + shareConfig.getName() + "'", 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);
        }

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

        // 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());
                }
            }
        }
    }

    public synchronized void updatePersonalisationDBs() {

        WGFactory.getInstance().closeSessions();

        // Map new and update existing databases
        Set<String> currentDBs = new HashSet<String>();

        Iterator<Domain> domains = _wgaConfiguration.getDomains().iterator();
        while (domains.hasNext()) {
          Domain domain = domains.next();
            PersonalisationDatabase persDBConfig = domain.getPersonalisation();
           
            // Ignore disabled pers databases
            if (persDBConfig == null) {
                continue;
            }
            if (!persDBConfig.isEnabled()) {
                continue;
            }
            DatabaseServer serverConfig = (DatabaseServer) _wgaConfiguration.getByUid(persDBConfig.getDbServer());
            if (serverConfig != null && !serverConfig.isEnabled()) {
                continue;
            }

            // Get or retrieve db
          WGDatabase db = null;
            if (this.personalisationdbs.containsKey(domain.getUid())) {
                db = this.personalisationdbs.get(domain.getUid());
            }
            else {
                db = retrievePersonalisationDB(domain);
                if (db == null) {
                    continue;
                }
                this.personalisationdbs.put(domain.getUid(), db);
            }

            currentDBs.add(domain.getUid());
           
        }

        // Unmap removed databases
        Set<String> removedDBs = new HashSet<String>(this.personalisationdbs.keySet());
        removedDBs.removeAll(currentDBs);
        Iterator<String> removedIt = removedDBs.iterator();
        while (removedIt.hasNext()) {
            String domain = removedIt.next();
            removePersonalisationDB(domain);
        }

    }
   

    private String defaultMediaKey = null;

    private static boolean _finalBuild;

    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 Analyzer defaultAnalyzer = new StandardAnalyzer();

    // 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> eventListeners = Collections.synchronizedList(new LinkedList<WGACoreEventListener>());

    // 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, String> _globalDbOptions;

    private Map<String, String> _globalPublisherOptions;

    private JmxManager _wgaMonitor;



    private LogServer _logServer;



    private ResourceConfiguration _resourceConfiguration;

  private WGAMailConfiguration _mailConfig;

    private Cache designFileCache;

    private WebTMLCache _webTMLCache = null;

    private void initReadGeneralConfig(boolean update) throws CacheException {

        // WGAPI Data Cache
        int dataCache = _wgaConfiguration.getWgapiDataCacheSize();
        if (dataCache != WGFactory.getInstance().getCacheCapacity()) {
            getLog().info("WGAPI Data Cache Capacity is set to " + dataCache + " entries" + (WGFactory.getInstance().isCacheInitialized() ? " (ineffective until a WGAPI data cache reset)" : ""));
            WGFactory.getInstance().setCacheCapacity(dataCache);
        }
     
        // WebTML Cache
        int tmlCache = _wgaConfiguration.getWebtmlCacheSize();
        if (_webTMLCache == null) {
            _webTMLCache = new WebTMLCache(tmlCache);
            getLog().info("WebTML Cache Capacity is set to " + tmlCache + " entries");
        }
        else if (_webTMLCache.getCapacity() != tmlCache){
            _webTMLCache.setCapacity(tmlCache);
            getLog().info("WebTML Cache Capacity is set to " + tmlCache + " entries (ineffective until a 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);
                }
            }
            else {
                this.log.error("Logging directory " + loggingDir.getAbsolutePath() + " does not exist");
            }
        }
       
        initLogServer();


        // 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);
            }
        }

        // 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) {
            designFileCache = CacheFactory.createCache(CACHENAME_DESIGNFILES, _wgaConfiguration.getDesignConfiguration().getDesignFileCacheSize(), null);
        }

        // Custom core listeners - Get names here, instantiate later when
        // library loader is inited      
        if (update == false) {
          customCoreListenerClassnames = new HashSet(_wgaConfiguration.getCoreEventListeners());   
        }
       
        _globalDbOptions = _wgaConfiguration.getGlobalDatabaseOptions();
        _globalPublisherOptions = _wgaConfiguration.getGlobalPublisherOptions();
       
        // init mail configuration
        _mailConfig = WGAMailConfiguration.create(_wgaConfiguration.getMailConfiguration());     
    }



    private void initLogServer() {
       
        /* Currently deactivated since we have no remote applog viewer right now
        try {
            if(_logServer == null || !_logServer.isActive()) {
                _logServer = new LogServer(this);
                _logServer.start();
            }
        }
        catch (Exception e) {
            getLog().error("Error initializing remote applog server", e);
        }*/
       
       
    }

    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() {

        //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));
       
        log.info("Registering defaultAnalyzer to '" + defaultAnalyzer.getClass().getName() + "'");
       
    }

    private void initReadFilterMappings() {
       
        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) {
                log.warn("Filter " + modDef.getTitle(Locale.getDefault()) + " deactivated in current WGA runtime: " + e.getMessage());
            }
        }
       
        // 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 from language code '" + language + "' to '" + analyzerClassName + "'");
      Class analyzerClass = getLibraryLoader().loadClass(analyzerClassName);
        Analyzer analyzer = (Analyzer) analyzerClass.newInstance();
      analyzerMappings.put(language.toLowerCase(), analyzer);
    }
   
    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, ";");
        int idx = 0;
        String token;
        while (tokens.hasMoreTokens()) {
   
            token = tokens.nextToken();
   
            try {
                File tokenFile = new File(token);
                if (!tokenFile.exists()) {
                    tokenFile = new File(getConfigFile().getParent(), token);
                }
   
                if (tokenFile.exists()) {
                    token = tokenFile.toURL().toString();
                }
   
                if (!token.equals("")) {
                    jarsList.add(new URL(token));
                    log.info("Registering class library URL " + token);
                    idx++;
                }
            }
            catch (MalformedURLException e) {
                this.log.warn("Library entry is neither a valid URL nor an existing file: " + token, e);
            }
        }
    }
     
      // 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);
        }
        }
      }
     
      try {
          // add all jars from /WEB-INF/isolated
          Iterator resourcePaths = _context.getResourcePaths("/WEB-INF/isolated").iterator();
          while (resourcePaths.hasNext()) {
              jarsList.add(_context.getResource((String) resourcePaths.next()));
          }
   
            // 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();
        }
        catch (MalformedURLException e1) {
            log.fatal("Unable to create library loader! TMLScript will not work!", e1);
        }
    }

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

  private void initTimerTasks() {

        // Timer for resetting ip blocking info every 30 minutes
        this.timer = new Timer();
        TimerTask task = new CleanupTask();
        long delay = 1000 * 60;
        this.timer.scheduleAtFixedRate(task, delay, delay);


        // Timer task for config file update
        task = new ConfigUpdateTask(this);
        delay = 1000 * 5;
        this.timer.scheduleAtFixedRate(task, delay, delay);
       
        // Timer task for monitoring developer plugins
        String devPluginsPath = System.getProperty(SYSPROPERTY_DEVELOPER_PLUGINS);
        if (devPluginsPath != null) {
            File devPluginsFolder = getWGAFile(devPluginsPath);
            if (devPluginsFolder.exists() && devPluginsFolder.isDirectory()) {
                task = new DevPluginsMonitoringTask(devPluginsFolder);
                delay = 1000 * 10;
                this.timer.scheduleAtFixedRate(task, delay, delay);
            }
        }

    }
   
   
    private boolean isDBAllowed(Class implClass, String dbKey) {
      return true;
    }
   
    private boolean isPersonalisationDBAllowed(Class implClass, String domain) {
      return true;
    }   
   
    private boolean isAllowedDBImplForXMLPublisher(Class implClass) {
      return true;
    }
   


   
    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 boolean hasPublisherLicense() {
      return true;
    }
   
    public boolean areElementsAllowed() {
      return true;
    }

    public boolean isPersonalisationEnabled() {
        return true;
    }
   
    public boolean hasBrowserInterfaceLicense() {
        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);

            // Creating logger
            this.log = Logger.getLogger("wga");
            initLoggingFile();
            String debug = System.getProperty(SYSPROPERTY_DEBUG);
            if (debug != null) {
                Logger logger = Logger.getLogger(debug);
                logger.setLevel(Level.DEBUG);
            }

            // Try to retrieve build properties
            _finalBuild = true;
            _buildSignature = "NOSIGNATURE";
            try {
                InputStream buildPropIn = _context.getResourceAsStream("/WEB-INF/wgabuild.properties");
                Properties props = new Properties();
                props.load(buildPropIn);
                if (props.containsKey("finalBuild")) {
                    _finalBuild = Boolean.valueOf(props.getProperty("finalBuild")).booleanValue();
                }
                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 + " InnovationGate 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 2.3, JSP Engine 1.2");
                this.servletPlatform = 2.2;
                this.jspPlatform = 1.1;
            }
            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);
            this.getServletContext().setAttribute(WGACore.ATTRIB_DEPLOYER, this.deployer);

            if (this.eventManager == null) {
                this.eventManager = new EventManager(this);
            }

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

            // Try to instantiate JmxManager. We must test if jmx classes are
            // available
            try {
                Class.forName("java.lang.management.ManagementFactory");
                _wgaMonitor = new JmxManager(new WGAInformation(this), new ObjectName("de.innovationgate.WGAMonitor:context=" + arg0.getServletContext().getServletContextName() + ",name=Information"));
                if (!JmxManager.JMX_DISABLED) {
                log.info("WGA JMX metrics enabled");
            }
            }
            catch (UnsupportedOperationException e) {
                log.info("WGA JMX metrics disabled because no JMX MBean server available");
            }
            catch (ClassNotFoundException e) {
                log.warn("WGA JMX metrics are not enabled since the neccessary API is not available");
            }
            catch (Throwable e) {
                log.warn("Error enabling WGA JMX metrics: " + e.getClass().getName() + " - " + e.getMessage());
            }

            // 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 (Exception e) {
            log.fatal("Fatal error initializing wga", e);
            e.printStackTrace();
        }

    }

    public ServletContext getServletContext() {
        return _context;
    }

    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);
        }

        if (isFinalBuild()) {
            output.append(" (Build ").append(WGAVersion.WGAPUBLISHER_BUILD_VERSION).append(")");
        }
        else {
            output.append(" (Interim version ").append(WGACore.getBuildSignature()).append(" of build ").append(WGAVersion.WGAPUBLISHER_BUILD_VERSION).append(")");
        }

        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);
            }
            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();
           
            if (_wgaMonitor != null) {
                _wgaMonitor.unregister();
            }
           
            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() {
        log.info(WGAVersion.WGAPUBLISHER_PRODUCT_NAME + " starting shutdown...");
        fireCoreEvent(new WGACoreEvent(WGACoreEvent.TYPE_PRE_SHUTDOWN, null, this));

        WGPDispatcher dispatcher = getDispatcher();
        if (dispatcher != null) {
            dispatcher.setServePages(false);
        }
       
        // Kill all timer tasks
        try {
            _quartzScheduler.shutdown(true);
        }
        catch (SchedulerException e) {
            getLog().error("Exception shutting down WGA scheduler", e);
        }
        _quartzScheduler = null;

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

        // Cleanup lucene manager
        if (luceneManager != null) {
            luceneManager.destroy();
            luceneManager = null;
        }

        if (_externalFileMaintenanceTask != null) {
            _externalFileMaintenanceTask.stop();
            _externalFileMaintenanceTask = null;
        }
       
        // fire pre disconnect event
        fireCoreEvent(new WGACoreEvent(WGACoreEvent.TYPE_SHUTDOWN_PRE_DISCONNECT, null, this));

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

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

        // Close domains (especially their auth modules)
        closeDomainConfigs(this.domainConfigs);
       
        unDeployErrorPage();
       
        WGHierarchicalDatabase.removeCoreListener(_hdbCoreListener);
       
        // fire post disconnect event
        fireCoreEvent(new WGACoreEvent(WGACoreEvent.TYPE_SHUTDOWN_POST_DISCONNECT, null, this));

        // Close access logger
        if (_accessLogger != null) {
            _accessLogger.close();
        }
       
        // Cleanup deployer
        deployer.shutdown();
       
        // Cleanup statistics
        if (_usageStatistics != null) {
            _usageStatistics.dispose();
            _usageStatistics = null;
        }

        // Remove all core listeners
        eventListeners.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);
        }

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

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

    // 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_DEPLOYER = "Deployer";

    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_PROFILE = "Profile:";

    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_TMLCONTEXTS = "TagContexts";





   

    public static final String ATTRIB_PROFILING_ENABLED = "ProfilingEnabled";

    public static final String ATTRIB_PROFILING_FILE = "ProfilingFile";

    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_INITIALIZING_ENABLED = "InitializingEnabled";

    public static final String ATTRIB_INITIALIZING_ROOT = "InitializingRoot";


    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_ROOT_TAG = "RootTag";

    public static final String ATTRIB_LOGINERROR = "LoginError";

    public static final String ATTRIB_META_MAPPINGS = "$MetaMappings";

    public static final String ATTRIB_ITEM_MAPPINGS = "$ItemMappings";

    public static final String ATTRIB_SERVLETRESPONSE = "OutputStream";

    public static final String ATTRIB_ACTIONKEYNR = "ActionKeyNr";

    public static final String ATTRIB_REDIRECT = "Redirect";

    public static final String ATTRIB_REQUESTTIME = "RequestTime";

    public static final String SESSION_ACTIONS = "Actions";

    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 ATTRIB_TMLFORM = "TMLForm";

    public static final String ATTRIB_LASTFORM = "LastForm";
   
    /**
     * Request Variable. When set to true forces WGA to perform a regular login
     * and ignore other authentication information (like certificate, request user)
     */
    public static final String ATTRIB_FORCEREGULARLOGIN = "ForceRegularLogin";

    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_PERSSTATMODE = "PersStatMode";

    public static final String DBATTRIB_FILEEXPIRATION_MINUTES = "FileExpirationMinutes";

    public static final String DBATTRIB_FILECACHE = "FileCache";

    public static final String DBATTRIB_FILECACHE_THRESHOLD = "FileCacheThreshold";



    public static final String ATTRIB_BI_COLLECTIONS_SHOW_RELEASED_ONLY = "BICollectionsShowReleasedOnly";
   
    /**
     * Specifies the maximum number of entries in database file cache
     */
    public static final String DBATTRIB_FILECACHE_ENTRIES = "FileCacheEntries";


    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_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";

    // 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"));

    Logger log = null;

    private Date instanceActiveSince;
   
    // domain configurations mapped by domainname
    private Map<String, DomainConfiguration> domainConfigs;

    private org.quartz.Scheduler _quartzScheduler;

    private ArrayList<Object> customCoreListeners;

    private SystemContainerManager _systemContainerManager;

    private File _wgaDataDir;

  private WGAConfiguration _wgaConfiguration;

    private WGADesignManager _designManager;

    private ModuleRegistry _moduleRegistry = null;

    private WGALoggerWrapper _accessLogger;

  private WGHierarchicalDatabaseCoreListener _hdbCoreListener;



    private ExternalFileServingMaintenanceTask _externalFileMaintenanceTask;

    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 wga 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");
            }
           
                       
            // 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();

            // Basic initializations
            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);
            }

            // retrieve general configuration
            logCategoryInfo("General Configuration", 1);
            initReadGeneralConfig(false);
           
            // Initialize system container manager
            _systemContainerManager = new SystemContainerManager(this);
           
            // Retrieve media key and element mappings
            logCategoryInfo("Mappings", 1);
            initReadMappings();
           
            // Create expression engines
            ExpressionEngineFactory.createEngines();
           
            // Initialize custom core listeners
            initCustomCoreListeners();
           
            // Read domain configurations
            logCategoryInfo("Domains", 1);
            Map<String, DomainConfiguration> newDomainConfigs = initReadDomains();
           
            deployErrorPage();
           
            // Init scheduler before db connection, so system containers in dbs can add jobs
            _scheduler = new Scheduler(this);
           
            // fire pre connect event
            fireCoreEvent(new WGACoreEvent(WGACoreEvent.TYPE_STARTUP_PRE_CONNECT, null, this));

            // connect plugins
            logCategoryInfo("Plugins", 1);
            updatePlugins(newDomainConfigs);
           
            // Init module registry
            logCategoryInfo("Modules", 1);
            initModuleRegistry();
           
            // Some tasks that adapt options in registry to those configured in the WGA configuration
            adaptConfigurationToRegistry(configMigrated);
           
            // Init filter mappings (which may be feeded from registry)
            logCategoryInfo("Filter mappings", 1);
            initReadFilterMappings();
           
            // Init access logger - Must be after modreg so JDBC drivers from mod dependencies are already loaded
            initAccessLogger();
           
            // Startup domain configurations (must be after plugin connection so that domains can use plugin-provided functionalities)
            logCategoryInfo("Domain authentications", 1);
            initStartupDomains(newDomainConfigs);
           
            // Init design manager
            logCategoryInfo("Design Sources", 1);
            _designManager = new WGADesignManager(this, _wgaConfiguration.getDesignConfiguration().getDesignSources());
           
            // Init mail service
            WGFactory.setMailService(new WGAMailService(this));
           
            // open database servers
            logCategoryInfo("Database servers", 1);
            updateDatabaseServers();
           
            // open content databases
            logCategoryInfo("Web applications and data sources", 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)");
            }
           
            updateContentDBs(false);

            // open personalisation databases
            logCategoryInfo("Domain Personalisation Databases", 1);
            updatePersonalisationDBs();

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

            // Declare timer tasks
            initTimerTasks();
           
            // Load scheduler jobs after db connection, so they can refer the connected dbs
            logCategoryInfo("Scheduler", 1);
            updateScheduler();

            // Init shares
            logCategoryInfo("Shares", 1);
            updateShares();
           
            this.getServletContext().setAttribute(WGACore.ATTRIB_CONTENTDBS, this.contentdbs);
           
            // init TestCore
            initTestCore();


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

            initExternalFileServing();
           
            // start external file serving maintenance
            _externalFileMaintenanceTask = new ExternalFileServingMaintenanceTask(this);
            _externalFileMaintenanceTask.start();
           
            // 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);
            }
        }
    }

    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);
                _accessLogger = null;
            }  
        }
       
    }

   
    private void updateDatabaseServers() {

        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());
                }
                catch (Exception e) {
                    getLog().error("Exception instantiating  database server", 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());
                    WGDatabaseServer server = (WGDatabaseServer) getModuleRegistry().instantiate(serverClass);
                    server.init(databaseServer.getUid(), databaseServer.getTitle(), databaseServer.getOptions());
                    newServers.put(server.getUid(), server);
                    getLog().info("Registering database server '" + server.getTitle(Locale.getDefault()) + "'");
                }
                catch (Exception e) {
                    getLog().error("Exception instantiating database server", e);
                }
            }
        }
       
        _databaseServers = newServers;
       
       
    }
   
    private Map<String,WGDatabaseServer> _databaseServers = new ConcurrentHashMap<String, WGDatabaseServer>();

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

    private void initModuleRegistry() {

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

        updateModuleDefinitions(true);

        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 (Exception 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);
      

       
        Map<ModuleDefinition,Exception> dependencyFailures = new HashMap<ModuleDefinition, Exception>();
       
        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 (Exception 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 (Exception 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 (Exception 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 (Exception 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 (Exception 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 (Exception 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 (Exception 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 (Exception 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 (Exception e) {
                    dependencyFailures.put(moduleDefinition, e);
                }
            }
        }
       
        Iterator<Map.Entry<ModuleDefinition,Exception>> 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,Exception> failure = failureMods.next();
                try {
                    getLog().info("- " + failure.getKey().getTitle(Locale.ENGLISH) + ": " + failure.getValue().getMessage());
                }
                catch (Exception e) {
                    getLog().error("- " + failure.getKey().getImplementationClass() + ": Exception while logging dependency failure", e);
                }
               
            }
        }

       

       
    }

    public void updateModuleDefinitions(boolean initialRegistration) {
       
        // Search for new module definitions
        try {
            _moduleRegistry.searchModuleDefinitions();
        }
        catch (IOException e) {
            getLog().error("Exception searching module definitions", e);
        }
       
        // Post processings
       
        // Instantiate registered HTML head inclusions
        List<HTMLHeadInclusion> htmlHeadInclusions = new ArrayList<HTMLHeadInclusion>();
        for (ModuleDefinition def : _moduleRegistry.getModulesForType(HTMLHeadInclusionModuleType.class).values()) {
            try {
                HTMLHeadInclusion inc = (HTMLHeadInclusion) _moduleRegistry.instantiate(def);
                htmlHeadInclusions.add(inc);
            }
            catch (Throwable e) {
                getLog().error("Exception processing HTML head inclusion " + def.getTitle(Locale.getDefault()), e);
            }
        }
        _htmlHeadInclusions = htmlHeadInclusions;
       
    }

    private void createDefaultWGAConfiguration(File file) throws Exception {
      log.info("Creating default wga configuration in file '" + file.getAbsolutePath() + "'.");
      WGAConfiguration defaultConfig = WGAConfiguration.createDefaultConfig();
      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() {

        // 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, DomainConfiguration> initReadDomains() {

        // Map new configs
        Map<String, DomainConfiguration> newDomainConfigs = new HashMap<String, DomainConfiguration>();
        Iterator<Domain> domains = _wgaConfiguration.getDomains().iterator();
        while (domains.hasNext()) {
            Domain domain = domains.next();
            DomainConfiguration domainConfig = new DomainConfiguration(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.");
                continue;
            }
           
            newDomainConfigs.put(domainConfig.getName(), domainConfig);
            this.log.info("Reading configuration of domain '" + domainConfig.toString() + "'");
           
        }
       
        return newDomainConfigs;
    }
   
    private void initStartupDomains(Map<String, DomainConfiguration> newDomainConfigs)  {

        // Initialize new domain configurations (their auth modules, that is)
        Iterator<DomainConfiguration> doms = newDomainConfigs.values().iterator();
        while (doms.hasNext()) {
            DomainConfiguration cfg = doms.next();
            logCategoryInfo("Domain " + cfg.getName(), 2);
            try {
                cfg.init();
                if (cfg.getAuthModule() != null) {
                    getLog().info("Uses: " + cfg.getAuthModule().getAuthenticationSource());
                }
                else {
                    getLog().info("Uses no authentication");
                }
            }
            catch (Exception e) {
                getLog().error("Exception initializing authentication for domain " + cfg.getName(), e);
            }
        }
       
        // Replace old domain configs with new domain configs
        Map<String, DomainConfiguration> oldDomainConfigs = this.domainConfigs;
        this.domainConfigs = newDomainConfigs;
        closeDomainConfigs(oldDomainConfigs);
    }


    private void closeDomainConfigs(Map<String, DomainConfiguration> oldDomainConfigs) {
       
        if (oldDomainConfigs == null) {
            return;
        }
       
        for (DomainConfiguration cfg : oldDomainConfigs.values()) {
            try {
                cfg.destroy();
            }
            catch (Throwable e) {
                getLog().error("Exception closing domain " + cfg.getName(), e);
            }
        }
       
       
    }

    private void updateScheduler() {

        try {                      
            String loggingDirStr = _wgaConfiguration.getSchedulerConfiguration().getLoggingDir();
            if (loggingDirStr != null) {
                _scheduler.setLoggingDir(new File(loggingDirStr));
            }

            // Register all currently configured jobs
            Iterator<de.innovationgate.wga.config.Job> jobs = _wgaConfiguration.getSchedulerConfiguration().getJobs().iterator();
            List<String> currentJobs = new ArrayList<String>();
            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);
                }
            }

            // 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 schedules.", e);
        }

    }


    private void parseConfigFile() throws Exception {
      try {
        logCategoryInfo("Parsing 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,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 Set<LicenseBindingKey> authorLicenseBindings = Collections.synchronizedSet(new HashSet<LicenseBindingKey>());

    private File configFile;

    private long configFileLastModified;

    //private Document configDocument;

    private Timer timer;



    private long numberOfAuthorLicenses = 0;

    private WGAPluginSet pluginSet;

    private TMLScriptGlobalRegistry _tmlscriptGlobalRegistry = new TMLScriptGlobalRegistry();

    private int _status;
   
    private WGAFilter _filter;

  private String _errorPage;

    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 String ATTRIB_BI_VERSION3 = "BrowserInterfaceVersion3";
   
    public static final String ATTRIB_BI_INCLUDE_BI3_SCRIPTS = "BrowserInterfaceIncludeBI3Scripts";

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

    public static final String ATTRIB_PORTLETKEYS = "PortletKeys";

    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";

    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";



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





    public Logger getLog() {
        return log;
    }

    public Set<LicenseBindingKey> getAuthorLicenseBindings() {
        return authorLicenseBindings;
    }

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

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

    public Map<String, WGDatabase> getDesigndbs() {

        Map<String, WGDatabase> mapDesigndbs = new HashMap<String, WGDatabase>();
        Map<String, WGDatabase> mapContentdbs = this.getContentdbs();
        Iterator<String> iter = mapContentdbs.keySet().iterator();
        WGDatabase db = null;

        while (iter.hasNext()) {
            db = mapContentdbs.get(iter.next());

            if (db.isDesignRole()) {
                mapDesigndbs.put((String) db.getAttribute(WGACore.DBATTRIB_DBKEY), db);
            }
        }

        if (mapDesigndbs != null) {
            return mapDesigndbs;
        }
        else {
            return null;
        }
    }

    /**
     * returns all designdbs of the given domain
     *
     * @param domain
     * @return
     */
    public Map<String, WGDatabase> getDesigndbs(String domain) {
        Map<String, WGDatabase> designDBs = getDesigndbs();
        Iterator<String> it = designDBs.keySet().iterator();
        Map<String, WGDatabase> domainDesignDBs = new HashMap<String, WGDatabase>();
        while (it.hasNext()) {
            String currentDBKey = it.next();
            WGDatabase db = designDBs.get(currentDBKey);
            String currentDomain = (String) db.getAttribute(WGACore.DBATTRIB_DOMAIN);
            if (domain.equals(currentDomain)) {
                domainDesignDBs.put(currentDBKey, db);
            }
        }
        return domainDesignDBs;
    }

    public String getDefaultMediaKey() {
        return defaultMediaKey;
    }

    public WGPDeployer getDeployer() {
        return deployer;
    }



    public Date getInstanceActiveSince() {
        return instanceActiveSince;
    }

    public long getNumberOfAuthorLicenses() {
        return numberOfAuthorLicenses;
    }

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

    public boolean isProfilingEnabled() {
        return profilingEnabled;
    }

    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_HTML) || encode.equalsIgnoreCase(ENCODER_XML)) {
            formatter = new EncodingFormatter(encode);
        }
        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_NAMEPART)) {
            formatter = UniqueNamePartFormatter.INSTANCE;
        }
        else if (encode.equalsIgnoreCase(ENCODER_URL)) {
            formatter = new URLEncodingFormatter();
        }
        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) {
            ((TMLContextAwareFormatter) formatter).setContext(context);
        }

        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 getDebugDocumentsList(HttpSession session) {

        synchronized (session) {
            List debugDocuments = (List) session.getAttribute(WGACore.ATTRIB_TMLDEBUG_DOCUMENTS);
            if (debugDocuments == null) {
                debugDocuments = new ArrayList();
                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 List getSyncMappings() {
        return syncMappings;
    }

    public synchronized void updateConfig() throws Exception  {
        configFileLastModified = configFile.lastModified();
        parseConfigFile();

        // Update general config
        initReadGeneralConfig(true);
       
        // Update filter mappings
        initReadFilterMappings();
       
        // Read domain configurations
        Map<String, DomainConfiguration> newDomainConfigs = initReadDomains();
       
        deployErrorPage();
       
        // Update WGA plugin connections
        updatePlugins(newDomainConfigs);
       
        // Startup domain configurations (must be after plugin connection so that domains can use plugin-provided functionalities)
        initStartupDomains(newDomainConfigs);
       
        updateDatabaseServers();
       
        // Deploy new content dbs, undeploy removed ones, update queries and
        // mappings, update lucene config
        updateContentDBs(true);

        // Connect new personalisation dbs, disconnect removed ones, update user
        // classes
        updatePersonalisationDBs();

        // Update jobs
        updateScheduler();
       
        // Update shares
        updateShares();
       
        initAccessLogger();
       
        initExternalFileServing();
    }
   
    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 exists, could not be created or is not writable. External file serving will be disabled.");
                _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);
            }
        }
  }
 
  private void unDeployErrorPage() {
     if (_errorPage != null) {
             String erroPagePath = getServletContext().getRealPath(_errorPage);
             File errorPage = new File(erroPagePath);
             if (errorPage.exists()) {
                 errorPage.delete();
             }
         }
  }
   
    public synchronized void updatePlugins(Map<String, DomainConfiguration> domainConfigs) {
       
        Set newConnectedDBKeys = new HashSet();
       
        if (_wgaDataDir == null) {
            getLog().warn("Unable to handle WGA plugins bc. the WGA data dir could not be initialized!");
            return;
        }
       
        getLog().info("Handling WGA plugins");
       
       
        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.");
                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)
            installDefaultPlugins(currentPlugins, reinstallPluginsDir);
            uninstallRemovedDefaultPlugins();
           
            // Validate and connect plugins
            currentPlugins.validatePlugins();
            newConnectedDBKeys = currentPlugins.connectPlugins(domainConfigs);
           
            // Save plugins def file
            this.pluginSet.save();
           
            // Update module registry if it is already available
            if (_moduleRegistry != null) {
                updateModuleDefinitions(false);
            }
           
        }
        catch (Throwable e) {
            getLog().error("Error initializing plugins", e);
        }
       
        return;
       
    }
   
    private boolean uninstallRemovedDefaultPlugins() {
       
        boolean anythingRemoved = false;
        for (WGAPlugin plugin : getPluginSet().getPlugins()) {
            if (plugin.isDefaultPlugin() || plugin.getRegisteredFilePath().contains("${wga.devpluginsdir}")) {
                if (!plugin.getPluginFile().exists()) {
                    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.domainConfigs);
    }

    private boolean installDefaultPlugins(WGAPluginSet currentPlugins, boolean reinstallPluginsFolder) throws FileSystemException, MalformedURLException {
       
        try {
            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);
                        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;
                }
   
                // Previous to installation, check if the plugin is already installed, cancel if so. We want to fail silently here.
                String fileName = pluginURL.getFile();
                int lastSlashPos = fileName.lastIndexOf("/");
                if (lastSlashPos != -1) {
                    fileName = fileName.substring(lastSlashPos + 1);
                }
               
                if (!fileName.endsWith(PluginConfig.WGAPLUGIN_SUFFIX)) {
                    continue;
                }
               
                File pluginFile = new File(getServletContext().getRealPath(pluginPath));
                if (pluginFile.getName().contains("de.innovationgate.Management-")) {
                    managementPluginFile = pluginFile;
                }
               
                try {
                    WGAPlugin.Configuration config = WGAPlugin.loadConfiguration(pluginFile);
                    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);
                            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;
                            }
                        }
                       
                        File resolvedFile = WGUtils.resolveDirLink(file);
                        try {
                            WGAPlugin.Configuration config = WGAPlugin.loadConfiguration(resolvedFile);
                            if (config != null) {
                                defaultPlugins.put(config.getCsConfig().getPluginConfig().getId(), resolvedFile);
                            }
                           
                        }
                        catch (Exception e) {
                            getLog().error("Exception installing registering plugin file " + resolvedFile.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;
                }
            }
           
            // STEP 3: Try to ensure that a Management plugin is installed and active
            List<WGAPlugin> mgmtPlugins = currentPlugins.getPluginsByUniqueName("de.innovationgate.Management");
            boolean anythingActive = false;
            for (WGAPlugin plugin : mgmtPlugins) {
                if (plugin.isActive() && plugin.isValid()) {
                    anythingActive = true;
                    break;
                }
            }
           
            if (!anythingActive && managementPluginFile != null) {
                currentPlugins.installPlugin(managementPluginFile, WGAPluginSet.UPDATESTRATEGY_UPDATE_KEEP_DATA, true);
                anythingInstalled = true;
            }
           
            return anythingInstalled;
        }
        catch (PluginException e) {
            if (e.getCause() != null) {
                getLog().error("Error installing default plugins", e);
            }
            else {
                getLog().error("Error installing default plugins: " + e.getMessage());
            }
            return false;
        }
    }

    private boolean installDefaultPlugin(WGAPluginSet currentPlugins, File file) {
        try {
            WGAPlugin.Configuration config = WGAPlugin.loadConfiguration(file);
            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) {

                // 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) throws 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 db;
                    }
                    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);
                }
            }
            // 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");
            dbOptions.put(WGDatabase.COPTION_USERCACHELATENCY, String.valueOf(_wgaConfiguration.getUserCacheLatencyMinutes()));
           
            // We try to automatically migrate plugin content stores to CS5 format
            dbOptions.put(WGDatabase.COPTION_CONTENT_STORE_VERSION, String.valueOf(WGDatabase.CSVERSION_WGA5));
           
            // 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);
            }
           
            // Connect
            getLog().info("Connecting plugin " + plugin.getPluginID().getUniqueName() + " Version " + plugin.getPluginID().getVersion().toString());
              
            try {
                db = WGFactory.getInstance().openDatabase(null, de.innovationgate.webgate.api.hsql.WGDatabaseImpl.class.getName(), plugin.buildDatabasePath(), null, null, 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
                db.setAttribute(DBATTRIB_PERSMODE, String.valueOf(pc.getPersonalisationMode()));
                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);
                db.setDesignProvider(new FileSystemDesignProvider(ref, this, db, plugin.getDesignURL().toString(), Collections.EMPTY_MAP));
                db.setAllowDesignModification(false);
              
                // 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 (Exception e) {
                    this.log.error("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();
                }
                                                               
                // 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);
               
                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);
                throw new InvalidPluginException(plugin, "Error connecting plugin" ,e);
            }
           
           
           
           
       
    }


    private void processPublisherOptions(WGDatabase db, Map<String, String> publisherOptions) {
       
        // 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());
               
        // 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);
        };
       
        // 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);
        }
       
        // 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) {
        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();
            }
        }
        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;
    }

    public static boolean isFinalBuild() {
        return _finalBuild;
    }

    /**
     * @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 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 DomainConfiguration getDomainConfig(Map<String, DomainConfiguration> configs, String name) {

        if (name.startsWith(PluginConfig.PLUGIN_DBKEY_PREFIX)) {
            DomainConfiguration dc = new DomainConfiguration(name);
            return dc;
        }
       
        return configs.get(name.toLowerCase());
    }
   
    public DomainConfiguration getDomainConfig(String name) {
        // Prevent nullpointer when this is called before domain init in startup
        if (this.domainConfigs != null) {
            return getDomainConfig(this.domainConfigs, name);
        }
        else {
            return null;
        }
    }

    public DomainConfiguration getDomainConfigForDatabase(WGDatabase db) {
       
        String domain = (String) db.getAttribute(DBATTRIB_DOMAIN);
        return getDomainConfig(domain);

    }

    /**
     * @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;
    }

    public DESEncrypter getDesEncrypter() {
        return desEncrypter;
    }

    public void setDesEncrypter(DESEncrypter desEncrypter) {
        this.desEncrypter = desEncrypter;
    }

    public void addEventListener(WGACoreEventListener listener) {

        if (!eventListeners.contains(listener)) {
            eventListeners.add(listener);
        }

    }

    public void removeEventListener(WGACoreEventListener listener) {
        eventListeners.remove(listener);
    }

    private void fireCoreEvent(WGACoreEvent event) {
       
        // Set core status
        if (event.getType() <= WGACoreEvent.WGACORE_STATE_MAXTYPE)  {
            _status = event.getType();
        }

       
        List listenersList = new ArrayList(eventListeners);
        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);
            }
        }

    }

    public WGAResourceBundleManager getResourceBundleManager(WGDatabase database) {

        synchronized (database) {
            WGAResourceBundleManager 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) {
            IPv4Address ip;
            try {
                ip = new IPv4Address(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, IPv4Address ip) {
        List clientRestrictions = (List) db.getAttribute(DBATTRIB_CLIENTRESTRICTIONS);
        if (clientRestrictions != null) {
            Iterator it = clientRestrictions.iterator();
            while (it.hasNext()) {
                de.innovationgate.utils.net.IPv4Restriction restriction = (de.innovationgate.utils.net.IPv4Restriction) 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);

    }

    /**
     * @deprecated since 4.1 the link encoding is equals getCharacterEncoding() - which defaults to UTF-8
     * @return Returns the tmlLinkEncoding.
     */
    public String getTmlLinkEncoding() {
        return getCharacterEncoding();
    }

    /**
     * @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(getDomainConfigForDatabase(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);
        }
    }

    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) {
       
       
        boolean bypassGlobalSettings = false;
        if (db.getDesignProvider() != null && db.getDesignProvider() instanceof DBDesignProvider) {
            bypassGlobalSettings = true;
        }
       
        // Update library loader. Can be bypassed if the system file container is from a provider DB.
        if (!info.isFromProviderDB()) {
            updateLibraryLoader();
            info.setEnfordedLibraryUpdate(true);
        }
       
        // If no cxconfig.xml we are finished here
        CSConfig csConfig = info.getCsConfig();
        if (csConfig == null) {
            return;
        }
       
        // 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());
                }
            }
           
           
            // 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());
                }
            }
           
            // 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 (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) {
                        getTmlscriptGlobalRegistry().registerGlobal(new TMLScriptGlobal(shortcut.getShortcut(), TMLScriptGlobal.TYPE_PACKAGE_OR_CLASS, shortcut.getReference()));
                    }
                }
               
            }
           
        }
       
        // Set WGA version compliance if not explicitly set via first level db option
        Set firstLevelOptions = (Set) db.getAttribute(DBATTRIB_FIRSTLEVELDBOPTIONS);
        if (!firstLevelOptions.contains(WGDatabase.COPTION_NOITEMBEHAVIOUR)) {
            db.getNoItemBehaviour().compliantTo(csConfig.getVersionCompliance());
        }
       
        // Add jobs
        Iterator jobs = csConfig.getJobDefinitions().iterator();
        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 {

        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");
        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);
        }
        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 = Class.forName(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);
        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.isEnfordedLibraryUpdate()) {
            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();
        }
    }

    public SystemContainerManager getSystemContainerManager() {
        return _systemContainerManager;
    }

    public boolean logout(String domain, javax.servlet.http.HttpSession session) throws WGAPIException {
       
        // Remove profiles of self-personalized dbs
        for (WGDatabase db : getDatabasesForDomain(domain)) {
            int persMode = Integer.parseInt((String) readPublisherOptionOrDefault(db, WGACore.DBATTRIB_PERSMODE));
            if (persMode == Constants.PERSMODE_LOGIN) {
                session.removeAttribute(WGPDispatcher.SESSION_PROFILENAME_INDIVIDUALDB + db.getDbReference());
            }
        }

        // Remove the sessionLogin for this domain
        Map<Object, DBLoginInfo> sessionLogins = getSessionLogins(session);
        if (domain != null) {
            sessionLogins.remove(domain);
        }
        else {
            sessionLogins.clear();
            session.removeAttribute("$defaultlogin");
        }
       
        return true;
    }
   
    public boolean login(String user, String password, String domain, HttpServletRequest request, HttpServletResponse response) throws LoginException, WGAPIException {
        // Get the domain configuration
        DomainConfiguration domainConfig = getDomainConfig(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());
           
            getSessionLogins(request.getSession()).put(domain, new DBLoginInfo(user, password));
           
            // F00004852
            // perform a reopenSession on all opened databases of this domain
            Iterator<WGDatabase> dbsInDomain = getDatabasesForDomain(domain).iterator();
            while (dbsInDomain.hasNext()) {
              WGDatabase db = dbsInDomain.next();
              if (db.isSessionOpen()) {
                db.reopenSession(user, password);
              }
            }
           
            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))) {
                   
                    Cookie sessionCookie = new Cookie(cookieName, sessionToken);
                    sessionCookie.setPath("/");
                    String sessionCookieDomain = (String) database.getAttribute(WGACore.DBATTRIB_SESSIONCOOKIEDOMAIN);
                    if (sessionCookieDomain != null) {
                        sessionCookie.setDomain(sessionCookieDomain);
                    }
                    else {
                        sessionCookie.setDomain(getSecondLevelDomain(request.getServerName()));
                    }
                    response.addCookie(sessionCookie);
                    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);
        }
       
    }

    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);
            }
        }
       
        // Unconfigured default ports
        if (protocol.equalsIgnoreCase("http")) {
            return 80;
        }
        else if (protocol.equalsIgnoreCase("https")) {
            return 443;
        }
       
        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());
        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>");
            strBody.append(notification.getMessage());
            String rootURL = getWgaConfiguration().getRootURL();
            if (rootURL != null) {
              strBody.append("<br><br>");
              strBody.append("<a href=\"" + rootURL + "/plugin-admin\">Open OpenWGA admin client</a><br>");
            }
            // append footer
            InetAddress localMachine = InetAddress.getLocalHost()
        String hostname = localMachine.getHostName();
        strBody.append("<br><br><b>System information:</b><br>");
        strBody.append("<b>WGA Publisher Version:</b> " + WGACore.getReleaseString() + "<br>");
        strBody.append("<b>Hostname:</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 );
            if (notification.isAttachLogfile()) {    
                   MimeBodyPart attachmentBody = new MimeBodyPart();
                  
                   StringWriter applog = new StringWriter();
                   int applogSize = getApplogLines();
                   int offset = applogSize - notification.getLogfileLines();
                   if (offset < 0) {
                     offset = 1;
                   }
                   writeApplogContent(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 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 void saveWgaConfiguration(final WGAConfiguration config) throws FileNotFoundException, Exception {

        // Everything done in a separate thread
        // updateConfig() will close database sessions and we need synchronisation on the whole block
        Runnable updateConfigTask = new Runnable() {
           
            public void run() {
               
                synchronized (WGACore.this) {
                    try {
                        // Replace WGA Configuration object
                        _wgaConfiguration = config;
                       
                        // First write to buffer, so serialisation/validation errors are catched before it is actually written to disk
                        ByteArrayOutputStream buffOut = new ByteArrayOutputStream();
                        WGAConfiguration.write(getWgaConfiguration(), buffOut);
                       
                        // Write buffered output to file
                        FileOutputStream configOut = new FileOutputStream(configFile);
                        configOut.write(buffOut.toByteArray());
                        configOut.flush();
                        configOut.close();
                       
                        // enforce configuratioe
                        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());   
                        }
                    }
                    catch (Exception e) {
                        getLog().error("Exception updating configuration", e);
                    }
                }
            }
        };
       
        Thread updateConfigThread = new Thread(updateConfigTask);
        updateConfigThread.start();
        updateConfigThread.join();
    }
   
    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_NONE);
       
        return formatters;
       
    }

    public WebTMLCache getWebTMLCache() {
        return _webTMLCache;
    }
 
    public Map getDomainConfigs() {
        return domainConfigs;
    }

    public static DynamicClassLoadingChain getLibraryClassLoadingChain() {
        return libraryClassLoadingChain;
    }

  public WGALoggerWrapper getAccessLogger() {
    return _accessLogger;
  }

  public void setProfilingEnabled(boolean profilingEnabled) {
    this.profilingEnabled = profilingEnabled;
  }

    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) {

        // Fetch option definition
        OptionDefinition optionDef = null;
       
        Class typeClass = ContentDatabasePublisherOptionsModuleType.class;
        Class collectorClass = ContentDatabasePublisherOptionsCollector.class;
        if (db.hasFeature(WGDatabase.FEATURE_FULLCONTENTFEATURES)) {
            typeClass = ContentStorePublisherOptionsModuleType.class;
            collectorClass = ContentStorePublisherOptionsCollector.class;
        }
       
        if (getModuleRegistry() != null) {
            ModuleDefinition pOptionDefs = getModuleRegistry().getModuleDefinition(typeClass, collectorClass);
            optionDef = pOptionDefs.getOptionDefinitions().get(name);
        }
       

        // Read the option value, default it if neccessary
        Object value = db.getAttribute(name);
        if (value == null && optionDef != null && optionDef.getDefaultValue() != null) {
            value = optionDef.getDefaultValue();
        }
       
        if (optionDef != null) {
            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);
            }
        }
        else {
            return value;
        }      
    }
   
    public WGAURLBuilder retrieveURLBuilder(HttpServletRequest request, WGDatabase db) {
        WGAURLBuilder builder = (WGAURLBuilder) request.getAttribute(WGACore.ATTRIB_URLBUILDER);
        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);
           
        }           
          }
         
          // Default/Fallback URL builder for normal web pages
          if (builder == null) {
            DefaultURLBuilder defBuilder = new DefaultURLBuilder();
            if ("true".equals(request.getParameter("forceClassicURL"))) {
                    defBuilder.setTitlePathAllowed(false);
                }
            builder = defBuilder;
          }     
         
            request.setAttribute(WGACore.ATTRIB_URLBUILDER, builder);
        }
        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());
    }
   
}
TOP

Related Classes of de.innovationgate.wgpublisher.WGACore$AsyncMailSender

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
y>