You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by bo...@apache.org on 2020/12/02 18:10:35 UTC

[myfaces-tobago] branch master updated: TOBAGO-2060: Tobago should be compatible with Spring Boot

This is an automated email from the ASF dual-hosted git repository.

bommel pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git


The following commit(s) were added to refs/heads/master by this push:
     new fe0a62b  TOBAGO-2060: Tobago should be compatible with Spring Boot
fe0a62b is described below

commit fe0a62b73b88084b9ea4ac841b822f55046cb03b
Author: Udo Schnurpfeil <ud...@irian.eu>
AuthorDate: Wed Dec 2 18:51:39 2020 +0100

    TOBAGO-2060: Tobago should be compatible with Spring Boot
    
    * Replacing CDI.current()
    * Refactoring TobagoConfig and its initialization
---
 .../apache/myfaces/tobago/config/TobagoConfig.java | 334 ++++++++++++++++---
 .../myfaces/tobago/context/TobagoContext.java      |  32 +-
 .../tobago/facelets/TobagoComponentHandler.java    |  10 +-
 .../internal/config/TobagoConfigBuilder.java       | 128 -------
 .../tobago/internal/config/TobagoConfigImpl.java   | 369 ---------------------
 .../tobago/internal/config/TobagoConfigLoader.java | 110 ++++++
 .../tobago/internal/config/TobagoConfigMerger.java | 113 +++++--
 .../tobago/internal/config/TobagoConfigSorter.java |  11 +-
 .../internal/renderkit/renderer/PageRenderer.java  |  10 +-
 .../myfaces/tobago/internal/util/ArrayUtils.java   |   3 +
 .../tobago/internal/util/AuthorizationHelper.java  |  41 +--
 .../tobago/lifecycle/SecretPhaseListener.java      |   9 +-
 .../org/apache/myfaces/tobago/webapp/Secret.java   |  26 +-
 .../SecretSessionListener.java}                    |  35 +-
 .../src/main/resources/META-INF/web-fragment.xml   |   3 +
 .../internal/config/AbstractTobagoTestBase.java    |   6 +-
 .../config/TobagoConfigMergingUnitTest.java        | 102 ++----
 .../config/TobagoConfigSorterUnitTest.java         |  66 ++--
 18 files changed, 646 insertions(+), 762 deletions(-)

diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/config/TobagoConfig.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/config/TobagoConfig.java
index e7d0594..831eada 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/config/TobagoConfig.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/config/TobagoConfig.java
@@ -20,80 +20,338 @@
 package org.apache.myfaces.tobago.config;
 
 import org.apache.myfaces.tobago.context.Theme;
+import org.apache.myfaces.tobago.context.ThemeImpl;
+import org.apache.myfaces.tobago.exception.TobagoConfigurationException;
 import org.apache.myfaces.tobago.internal.config.ContentSecurityPolicy;
 import org.apache.myfaces.tobago.internal.config.SecurityAnnotation;
+import org.apache.myfaces.tobago.internal.config.TobagoConfigFragment;
+import org.apache.myfaces.tobago.internal.config.TobagoConfigLoader;
+import org.apache.myfaces.tobago.internal.config.TobagoConfigMerger;
+import org.apache.myfaces.tobago.internal.config.TobagoConfigSorter;
 import org.apache.myfaces.tobago.sanitizer.Sanitizer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.enterprise.inject.spi.CDI;
+import javax.faces.application.Application;
 import javax.faces.context.FacesContext;
 import javax.servlet.ServletContext;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
-public abstract class TobagoConfig {
+public class TobagoConfig {
 
   private static final Logger LOG = LoggerFactory.getLogger(TobagoConfig.class);
 
-  /**
-   * @deprecated Since 5.0.0. Please use CDI.
-   */
-  @Deprecated
   public static final String TOBAGO_CONFIG = "org.apache.myfaces.tobago.config.TobagoConfig";
 
-  /**
-   * @deprecated Since 5.0.0. Please use CDI.
-   */
-  @Deprecated
+  private List<Theme> supportedThemes;
+  private List<String> supportedThemeNames;
+  private Theme defaultTheme;
+  private String defaultThemeName;
+  private Map<String, ThemeImpl> availableThemes;
+  private boolean createSessionSecret;
+  private boolean checkSessionSecret;
+  private boolean preventFrameAttacks;
+  private final ContentSecurityPolicy contentSecurityPolicy;
+  private SecurityAnnotation securityAnnotation;
+  private boolean setNosniffHeader;
+  private Map<String, String> defaultValidatorInfo;
+  private Sanitizer sanitizer;
+  private boolean decodeLineFeed;
+  private Map<String, String> mimeTypes;
+
+  private boolean locked = false;
+
   public static TobagoConfig getInstance(final FacesContext facesContext) {
-    TobagoConfig tobagoConfig = null;
-    try {
-      tobagoConfig = CDI.current().select(TobagoConfig.class).get();
-    } catch (Exception e) {
-      LOG.warn("No CDI!");
+    final Map<String, Object> applicationMap = facesContext.getExternalContext().getApplicationMap();
+
+    TobagoConfig tobagoConfig = (TobagoConfig) applicationMap.get(TOBAGO_CONFIG);
+    if (tobagoConfig != null) {
+      return tobagoConfig;
     }
+
+    tobagoConfig = new TobagoConfig((ServletContext) facesContext.getExternalContext().getContext());
+    applicationMap.put(TOBAGO_CONFIG, tobagoConfig);
+
+    return tobagoConfig;
+  }
+
+  public static TobagoConfig getInstance(final ServletContext servletContext) {
+    TobagoConfig tobagoConfig = (TobagoConfig) servletContext.getAttribute(TOBAGO_CONFIG);
     if (tobagoConfig != null) {
       return tobagoConfig;
-    } else {
-      // XXX not nice: this happens while unit tests and whenever???
-      return (TobagoConfig) facesContext.getExternalContext().getApplicationMap().get(TOBAGO_CONFIG);
     }
+
+    tobagoConfig = new TobagoConfig(servletContext);
+    servletContext.setAttribute(TOBAGO_CONFIG, tobagoConfig);
+
+    return tobagoConfig;
   }
 
   /**
-   * @deprecated Since 5.0.0. Please use CDI.
+   * @param servletContext From the container. If null, the WEB-INF/tobago-config.xml will be ignored.
+   * @param alternative Alternative tobago-config-files, only needed for testing.
    */
-  @Deprecated
-  public static TobagoConfig getInstance(final ServletContext servletContext) {
-    return CDI.current().select(TobagoConfig.class).get();
+  public TobagoConfig(final ServletContext servletContext, final String... alternative) {
+
+    supportedThemeNames = new ArrayList<>();
+    supportedThemes = new ArrayList<>();
+    availableThemes = new HashMap<>();
+    createSessionSecret = true;
+    checkSessionSecret = true;
+    preventFrameAttacks = true;
+    setNosniffHeader = true;
+    securityAnnotation = SecurityAnnotation.disable;
+    decodeLineFeed = true;
+    contentSecurityPolicy = new ContentSecurityPolicy(ContentSecurityPolicy.Mode.OFF.getValue());
+    mimeTypes = new HashMap<>();
+
+    // internal
+    final List<TobagoConfigFragment> fragments = new ArrayList<>();
+
+    try {
+      TobagoConfigLoader.load(fragments, servletContext, alternative);
+      TobagoConfigSorter.sort(fragments);
+      TobagoConfigMerger.merge(fragments, this);
+      initDefaultValidatorInfo();
+      lock();
+//todo?        servletContext.setAttribute(TobagoConfig.TOBAGO_CONFIG, this);
+    } catch (final Exception e) {
+      final String error = "Tobago can't be initialized! Application will not run correctly!";
+      LOG.error(error, e);
+      throw new TobagoConfigurationException(error, e);
+    }
   }
 
-  public abstract Theme getTheme(final String name);
+  /**
+   * Lock the configuration, so it cannot be modified any more.
+   */
+  public void lock() {
+    locked = true;
+    supportedThemes = Collections.unmodifiableList(supportedThemes);
+    for (final Theme theme : supportedThemes) {
+      ((ThemeImpl) theme).lock();
+    }
+    supportedThemeNames = Collections.unmodifiableList(supportedThemeNames);
+    availableThemes = Collections.unmodifiableMap(availableThemes);
+
+    contentSecurityPolicy.lock();
 
-  public abstract List<Theme> getSupportedThemes();
+    mimeTypes = Collections.unmodifiableMap(mimeTypes);
+  }
 
-  public abstract Theme getDefaultTheme();
+  private void checkUnlocked() throws IllegalStateException {
+    if (locked) {
+      throw new TobagoConfigurationException("The configuration must not be changed after initialization!");
+    }
+  }
 
-  public abstract boolean isCreateSessionSecret();
+  public void addSupportedThemeName(final String name) {
+    checkUnlocked();
+    supportedThemeNames.add(name);
+  }
 
-  public abstract boolean isCheckSessionSecret();
+  public List<String> getSupportedThemeNames() {
+    return supportedThemeNames;
+  }
 
-  /**
-   * @deprecated But needed to support frame security in IE11. Is replaced by CSP "frame-ancestors".
-   */
-  @Deprecated
-  public abstract boolean isPreventFrameAttacks();
+  public Theme getTheme(final String name) {
+    if (name == null) {
+      LOG.debug("searching theme: null");
+      return defaultTheme;
+    }
+    if (defaultTheme != null && defaultTheme.getName().equals(name)) {
+      return defaultTheme;
+    }
+    for (final Theme theme : supportedThemes) {
+      if (theme.getName().equals(name)) {
+        return theme;
+      }
+    }
+    LOG.debug("searching theme '{}' not found. Using default: {}", name, defaultTheme);
+    return defaultTheme;
+  }
+
+  public void setDefaultThemeName(final String defaultThemeName) {
+    checkUnlocked();
+    this.defaultThemeName = defaultThemeName;
+  }
+
+  public String getDefaultThemeName() {
+    return defaultThemeName;
+  }
+
+  public List<Theme> getSupportedThemes() {
+    return supportedThemes;
+  }
+
+  public void setDefaultTheme(final Theme defaultTheme) {
+    this.defaultTheme = defaultTheme;
+  }
+
+  public Theme getDefaultTheme() {
+    return defaultTheme;
+  }
+
+  public void addAvailableTheme(final ThemeImpl availableTheme) {
+    checkUnlocked();
+    final String name = availableTheme.getName();
+    if (availableThemes.containsKey(name)) {
+      final ThemeImpl base = availableThemes.get(name);
+      availableThemes.put(name, ThemeImpl.merge(base, availableTheme));
+    } else {
+      availableThemes.put(name, availableTheme);
+    }
+  }
+
+  public Map<String, ThemeImpl> getAvailableThemes() {
+    return availableThemes;
+  }
+
+  public boolean isCreateSessionSecret() {
+    return createSessionSecret;
+  }
+
+  public void setCreateSessionSecret(final boolean createSessionSecret) {
+    checkUnlocked();
+    this.createSessionSecret = createSessionSecret;
+  }
+
+  public boolean isCheckSessionSecret() {
+    return checkSessionSecret;
+  }
+
+  public void setCheckSessionSecret(final boolean checkSessionSecret) {
+    checkUnlocked();
+    this.checkSessionSecret = checkSessionSecret;
+  }
+
+
+  public boolean isPreventFrameAttacks() {
+    return preventFrameAttacks;
+  }
+
+  public void setPreventFrameAttacks(final boolean preventFrameAttacks) {
+    checkUnlocked();
+    this.preventFrameAttacks = preventFrameAttacks;
+  }
+
+  public ContentSecurityPolicy getContentSecurityPolicy() {
+    return contentSecurityPolicy;
+  }
+
+  public boolean isSetNosniffHeader() {
+    return setNosniffHeader;
+  }
 
-  public abstract ContentSecurityPolicy getContentSecurityPolicy();
+  public void setSetNosniffHeader(final boolean setNosniffHeader) {
+    checkUnlocked();
+    this.setNosniffHeader = setNosniffHeader;
+  }
+
+  public SecurityAnnotation getSecurityAnnotation() {
+    return securityAnnotation;
+  }
 
-  public abstract boolean isSetNosniffHeader();
+  public void setSecurityAnnotation(final SecurityAnnotation securityAnnotation) {
+    checkUnlocked();
+    this.securityAnnotation = securityAnnotation;
+  }
 
-  public abstract SecurityAnnotation getSecurityAnnotation();
+  public Map<String, String> getDefaultValidatorInfo() {
+    // TODO: if the startup hasn't found a FacesContext and Application, this may depend on the order of the listeners.
+    if (defaultValidatorInfo == null) {
+      initDefaultValidatorInfo();
+    }
+    return defaultValidatorInfo;
+  }
 
-  public abstract Sanitizer getSanitizer();
+  public Sanitizer getSanitizer() {
+    return sanitizer;
+  }
 
-  public abstract boolean isDecodeLineFeed();
+  public void setSanitizer(final Sanitizer sanitizer) {
+    checkUnlocked();
+    this.sanitizer = sanitizer;
+  }
 
-  public abstract Map<String, String> getMimeTypes();
+  public boolean isDecodeLineFeed() {
+    return decodeLineFeed;
+  }
+
+  public void setDecodeLineFeed(final boolean decodeLineFeed) {
+    checkUnlocked();
+    this.decodeLineFeed = decodeLineFeed;
+  }
+
+  public Map<String, String> getMimeTypes() {
+    return mimeTypes;
+  }
+
+  private void initDefaultValidatorInfo() {
+    if (defaultValidatorInfo != null) {
+      checkUnlocked();
+    }
+    final FacesContext facesContext = FacesContext.getCurrentInstance();
+    if (facesContext != null) {
+      try {
+        final Application application = facesContext.getApplication();
+        final Map<String, String> map = application.getDefaultValidatorInfo();
+        if (map.size() > 0) {
+          defaultValidatorInfo = Collections.unmodifiableMap(map);
+        } else {
+          defaultValidatorInfo = Collections.emptyMap();
+        }
+      } catch (final Exception e) {
+        LOG.error("Can't initialize default validators (this happens with JBoss GateIn 3.6.0).", e);
+        defaultValidatorInfo = Collections.emptyMap();
+      }
+    }
+  }
+
+  @Override
+  public String toString() {
+    final StringBuilder builder = new StringBuilder();
+    builder.append("TobagoConfigImpl{");
+    builder.append("\nsupportedThemes=[");
+    for (final Theme supportedTheme : supportedThemes) {
+      builder.append(supportedTheme.getName());
+      builder.append(", ");
+    }
+    builder.append("], \ndefaultTheme=");
+    builder.append(defaultTheme != null ? defaultTheme.getName() : null);
+    builder.append(", \navailableThemes=");
+    builder.append(availableThemes.keySet());
+    builder.append(", \ncreateSessionSecret=");
+    builder.append(createSessionSecret);
+    builder.append(", \ncheckSessionSecret=");
+    builder.append(checkSessionSecret);
+    builder.append(", \npreventFrameAttacks=");
+    builder.append(preventFrameAttacks);
+    builder.append(", \ncontentSecurityPolicy=");
+    builder.append(contentSecurityPolicy);
+    builder.append(", \nsecurityAnnotation=");
+    builder.append(securityAnnotation);
+    builder.append(", \nsetNosniffHeader=");
+    builder.append(setNosniffHeader);
+    builder.append(", \ndefaultValidatorInfo=");
+    builder.append(defaultValidatorInfo);
+    builder.append(", \nsanitizer=");
+    builder.append(sanitizer);
+    builder.append(", \ndecodeLineFeed=");
+    builder.append(decodeLineFeed);
+    // to see only different (ignore alternative names for the same theme)
+    builder.append(", \nthemes=");
+    final Set<Theme> all = new HashSet<>(availableThemes.values());
+    builder.append(all);
+    builder.append(", \nmimeTypes=");
+    builder.append(mimeTypes);
+    builder.append('}');
+    return builder.toString();
+  }
 }
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/context/TobagoContext.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/context/TobagoContext.java
index 2cb7f76..6fa2858 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/context/TobagoContext.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/context/TobagoContext.java
@@ -21,7 +21,6 @@ package org.apache.myfaces.tobago.context;
 
 import org.apache.myfaces.tobago.config.TobagoConfig;
 import org.apache.myfaces.tobago.internal.util.CookieUtils;
-import org.apache.myfaces.tobago.util.VariableResolverUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -29,12 +28,12 @@ import javax.enterprise.context.RequestScoped;
 import javax.faces.component.UIViewRoot;
 import javax.faces.context.ExternalContext;
 import javax.faces.context.FacesContext;
-import javax.inject.Inject;
 import javax.inject.Named;
 import javax.servlet.http.HttpServletRequest;
 import java.io.Serializable;
 import java.lang.invoke.MethodHandles;
 import java.util.Locale;
+import java.util.Map;
 import java.util.ResourceBundle;
 
 @Named
@@ -45,7 +44,6 @@ public class TobagoContext implements Serializable {
 
   private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
-  @Inject
   private TobagoConfig tobagoConfig;
   private Theme theme;
   private UserAgent userAgent;
@@ -58,9 +56,10 @@ public class TobagoContext implements Serializable {
    */
   @Deprecated
   public ResourceBundle getResourceBundle() {
-    final UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot();
+    final FacesContext facesContext = FacesContext.getCurrentInstance();
+    final UIViewRoot viewRoot = facesContext.getViewRoot();
     final Locale locale = viewRoot != null
-        ? viewRoot.getLocale() : FacesContext.getCurrentInstance().getApplication().getDefaultLocale();
+        ? viewRoot.getLocale() : facesContext.getApplication().getDefaultLocale();
     return ResourceBundle.getBundle("tobagoResourceBundle", locale);
   }
 
@@ -69,17 +68,17 @@ public class TobagoContext implements Serializable {
    */
   @Deprecated
   public ResourceBundle getMessageBundle() {
-    final UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot();
+    final FacesContext facesContext = FacesContext.getCurrentInstance();
+    final UIViewRoot viewRoot = facesContext.getViewRoot();
     final Locale locale = viewRoot != null
-        ? viewRoot.getLocale() : FacesContext.getCurrentInstance().getApplication().getDefaultLocale();
+        ? viewRoot.getLocale() : facesContext.getApplication().getDefaultLocale();
     return ResourceBundle.getBundle("org.apache.myfaces.tobago.context.TobagoMessage", locale);
   }
 
-  /**
-   * @deprecated since 5.0.0. Please get/inject {@link TobagoConfig} directly by CDI.
-   */
-  @Deprecated
   public TobagoConfig getTobagoConfig() {
+    if (tobagoConfig == null) {
+      tobagoConfig = TobagoConfig.getInstance(FacesContext.getCurrentInstance());
+    }
     return tobagoConfig;
   }
 
@@ -97,7 +96,7 @@ public class TobagoContext implements Serializable {
         themeName = null;
       }
 
-      theme = tobagoConfig.getTheme(themeName);
+      theme = getTobagoConfig().getTheme(themeName);
       if (LOG.isDebugEnabled()) {
         LOG.debug("theme='{}'", theme.getName());
       }
@@ -147,6 +146,13 @@ public class TobagoContext implements Serializable {
   }
 
   public static TobagoContext getInstance(final FacesContext facesContext) {
-    return (TobagoContext) VariableResolverUtils.resolveVariable(facesContext, BEAN_NAME);
+    final Map<String, Object> requestMap = facesContext.getExternalContext().getRequestMap();
+    if (requestMap.containsKey(BEAN_NAME)) {
+      return (TobagoContext) requestMap.get(BEAN_NAME);
+    } else {
+      final TobagoContext tobagoContext = new TobagoContext();
+      requestMap.put(BEAN_NAME, tobagoContext);
+      return tobagoContext;
+    }
   }
 }
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/facelets/TobagoComponentHandler.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/facelets/TobagoComponentHandler.java
index 43939c8..65ab9d8 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/facelets/TobagoComponentHandler.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/facelets/TobagoComponentHandler.java
@@ -25,9 +25,7 @@ import org.apache.myfaces.tobago.config.TobagoConfig;
 import org.apache.myfaces.tobago.event.SheetStateChangeSource;
 import org.apache.myfaces.tobago.event.SortActionSource;
 import org.apache.myfaces.tobago.event.TabChangeSource;
-import org.apache.myfaces.tobago.internal.config.TobagoConfigImpl;
 
-import javax.enterprise.inject.spi.CDI;
 import javax.faces.component.EditableValueHolder;
 import javax.faces.component.UIComponent;
 import javax.faces.context.FacesContext;
@@ -78,8 +76,8 @@ public class TobagoComponentHandler extends ComponentHandler {
     }
   }
 
-  private void addDefaultValidators(final FacesContext context, final EditableValueHolder component) {
-    final TobagoConfigImpl tobagoConfig = (TobagoConfigImpl) CDI.current().select(TobagoConfig.class).get();
+  private void addDefaultValidators(final FacesContext facesContext, final EditableValueHolder component) {
+    final TobagoConfig tobagoConfig = TobagoConfig.getInstance(facesContext);
     final Map<String, String> validatorInfoMap = tobagoConfig.getDefaultValidatorInfo();
     if (validatorInfoMap.isEmpty()) {
       return;
@@ -93,12 +91,12 @@ public class TobagoComponentHandler extends ComponentHandler {
       }
       validatorInfoMap.forEach((key, value) -> {
         if (!classNames.contains(value)) {
-          component.addValidator(context.getApplication().createValidator(key));
+          component.addValidator(facesContext.getApplication().createValidator(key));
         }
       });
     } else {
       for (final String next : validatorInfoMap.keySet()) {
-        component.addValidator(context.getApplication().createValidator(next));
+        component.addValidator(facesContext.getApplication().createValidator(next));
       }
     }
   }
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/config/TobagoConfigBuilder.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/config/TobagoConfigBuilder.java
deleted file mode 100644
index 5b3617c..0000000
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/config/TobagoConfigBuilder.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.myfaces.tobago.internal.config;
-
-import org.apache.myfaces.tobago.config.TobagoConfig;
-import org.apache.myfaces.tobago.exception.TobagoConfigurationException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.xml.sax.SAXException;
-
-import javax.enterprise.context.ApplicationScoped;
-import javax.enterprise.inject.Produces;
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-import javax.xml.parsers.ParserConfigurationException;
-import java.io.IOException;
-import java.lang.invoke.MethodHandles;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.List;
-
-@Named
-@ApplicationScoped
-public class TobagoConfigBuilder {
-
-  private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
-
-  private static final String WEB_INF_TOBAGO_CONFIG_XML = "WEB-INF/tobago-config.xml";
-  private static final String META_INF_TOBAGO_CONFIG_XML = "META-INF/tobago-config.xml";
-
-  @Inject
-  private ServletContext servletContext;
-
-  private TobagoConfigImpl tobagoConfig;
-
-  @Produces
-  public TobagoConfig buildTobagoConfig() {
-    if (tobagoConfig == null) {
-      init();
-    }
-    return tobagoConfig;
-  }
-
-  private void init() {
-    final List<TobagoConfigFragment> configFragmentList = new ArrayList<>();
-    try {
-      configFromClasspath(configFragmentList);
-      configFromWebInf(configFragmentList);
-      final TobagoConfigSorter sorter = new TobagoConfigSorter(configFragmentList);
-      final TobagoConfigMerger merger = new TobagoConfigMerger(sorter.topologicalSort());
-      tobagoConfig = merger.merge();
-      // prepare themes
-      tobagoConfig.resolveThemes();
-      tobagoConfig.initDefaultValidatorInfo();
-      tobagoConfig.lock();
-      servletContext.setAttribute(TobagoConfig.TOBAGO_CONFIG, tobagoConfig);
-    } catch (final Exception e) {
-      final String error = "Tobago can't be initialized! Application will not run correctly!";
-      LOG.error(error, e);
-      throw new TobagoConfigurationException(error, e);
-    }
-  }
-
-  private void configFromWebInf(final List<TobagoConfigFragment> configFragmentList)
-      throws IOException, SAXException, ParserConfigurationException, URISyntaxException {
-
-    final URL url = servletContext.getResource("/" + WEB_INF_TOBAGO_CONFIG_XML);
-    if (url != null) {
-      configFragmentList.add(new TobagoConfigParser().parse(url));
-    }
-  }
-
-  private void configFromClasspath(final List<TobagoConfigFragment> configFragmentList)
-      throws ServletException {
-
-    try {
-      if (LOG.isInfoEnabled()) {
-        LOG.info("Searching for '" + META_INF_TOBAGO_CONFIG_XML + "'");
-      }
-      final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
-      final Enumeration<URL> urls = classLoader.getResources(META_INF_TOBAGO_CONFIG_XML);
-      while (urls.hasMoreElements()) {
-        final URL themeUrl = urls.nextElement();
-        try {
-          final TobagoConfigFragment fragment = new TobagoConfigParser().parse(themeUrl);
-          fragment.setUrl(themeUrl);
-          configFragmentList.add(fragment);
-
-          // tomcat uses jar
-          // weblogic uses zip
-          // IBM WebSphere uses wsjar
-          final String protocol = themeUrl.getProtocol();
-          if (!"file".equals(protocol) && !"jar".equals(protocol)
-              && !"zip".equals(protocol) && !"wsjar".equals(protocol)) {
-            LOG.warn("Unknown protocol '" + themeUrl + "'");
-          }
-        } catch (final Exception e) {
-          throw new Exception(e.getClass().getName() + " on themeUrl: " + themeUrl, e);
-        }
-      }
-    } catch (final Exception e) {
-      final String msg = "while loading ";
-      LOG.error(msg, e);
-      throw new ServletException(msg, e);
-    }
-  }
-}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/config/TobagoConfigImpl.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/config/TobagoConfigImpl.java
deleted file mode 100644
index c859d4b..0000000
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/config/TobagoConfigImpl.java
+++ /dev/null
@@ -1,369 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.myfaces.tobago.internal.config;
-
-import org.apache.myfaces.tobago.config.TobagoConfig;
-import org.apache.myfaces.tobago.context.Theme;
-import org.apache.myfaces.tobago.context.ThemeImpl;
-import org.apache.myfaces.tobago.exception.TobagoConfigurationException;
-import org.apache.myfaces.tobago.sanitizer.Sanitizer;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.faces.application.Application;
-import javax.faces.context.FacesContext;
-import java.lang.invoke.MethodHandles;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * <p>
- * Implementation of the Tobago configuration.
- * </p>
- * <p>
- * All setters must are protected, so EL can't modify this config.
- * </p>
- */
-//@Named("tobagoConfig") // todo
-//@ApplicationScoped // todo
-public class TobagoConfigImpl extends TobagoConfig {
-
-  private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
-
-  private List<Theme> supportedThemes;
-  private List<String> supportedThemeNames;
-  private Theme defaultTheme;
-  private String defaultThemeName;
-  private Map<String, ThemeImpl> availableThemes;
-  private boolean createSessionSecret;
-  private boolean checkSessionSecret;
-  private boolean preventFrameAttacks;
-  private final ContentSecurityPolicy contentSecurityPolicy;
-  private SecurityAnnotation securityAnnotation;
-  private boolean setNosniffHeader;
-  private Map<String, String> defaultValidatorInfo;
-  private Sanitizer sanitizer;
-  private boolean decodeLineFeed;
-  private Map<String, String> mimeTypes;
-
-  private boolean locked = false;
-
-  protected TobagoConfigImpl(String fixme) { // CDI workaround fixme
-    supportedThemeNames = new ArrayList<>();
-    supportedThemes = new ArrayList<>();
-    availableThemes = new HashMap<>();
-    createSessionSecret = true;
-    checkSessionSecret = true;
-    preventFrameAttacks = true;
-    setNosniffHeader = true;
-    securityAnnotation = SecurityAnnotation.disable;
-    decodeLineFeed = true;
-    contentSecurityPolicy = new ContentSecurityPolicy(ContentSecurityPolicy.Mode.OFF.getValue());
-    mimeTypes = new HashMap<>();
-  }
-
-  /**
-   * Lock the configuration, so it cannot be modified any more.
-   */
-  protected void lock() {
-    locked = true;
-    supportedThemes = Collections.unmodifiableList(supportedThemes);
-    for (final Theme theme : supportedThemes) {
-      ((ThemeImpl) theme).lock();
-    }
-    supportedThemeNames = Collections.unmodifiableList(supportedThemeNames);
-    availableThemes = Collections.unmodifiableMap(availableThemes);
-
-    contentSecurityPolicy.lock();
-
-    mimeTypes = Collections.unmodifiableMap(mimeTypes);
-  }
-
-  private void checkUnlocked() throws IllegalStateException {
-    if (locked) {
-      throw new TobagoConfigurationException("The configuration must not be changed after initialization!");
-    }
-  }
-
-  protected void addSupportedThemeName(final String name) {
-    checkUnlocked();
-    supportedThemeNames.add(name);
-  }
-
-  // TODO one init method
-  protected void resolveThemes() {
-    checkUnlocked();
-
-    if (defaultThemeName != null) {
-      defaultTheme = availableThemes.get(defaultThemeName);
-      checkThemeIsAvailable(defaultThemeName, defaultTheme);
-      if (LOG.isDebugEnabled()) {
-        LOG.debug("name = '{}'", defaultThemeName);
-        LOG.debug("defaultTheme = '{}'", defaultTheme);
-      }
-    } else {
-      int deep = 0;
-      for (final Map.Entry<String, ThemeImpl> entry : availableThemes.entrySet()) {
-        final Theme theme = entry.getValue();
-        if (theme.getFallbackList().size() > deep) {
-          defaultTheme = theme;
-          deep = theme.getFallbackList().size();
-        }
-      }
-      if (defaultTheme == null) {
-        final String error = "Did not found any theme! "
-            + "Please ensure you have a tobago-config.xml with a theme-definition in your "
-            + "theme JAR. Please add a theme JAR to your classpath. Usually "
-            + "tobago-theme-standard.jar in WEB-INF/lib";
-        LOG.error(error);
-        throw new TobagoConfigurationException(error);
-      } else {
-        if (LOG.isInfoEnabled()) {
-          LOG.info("Using default Theme {}", defaultTheme.getName());
-        }
-      }
-    }
-    if (!supportedThemeNames.isEmpty()) {
-      for (final String name : supportedThemeNames) {
-        final Theme theme = availableThemes.get(name);
-        checkThemeIsAvailable(name, theme);
-        supportedThemes.add(theme);
-        if (LOG.isDebugEnabled()) {
-          LOG.debug("name = '{}'", name);
-          LOG.debug("supportedThemes.last() = '{}'", supportedThemes.get(supportedThemes.size() - 1));
-        }
-      }
-    }
-  }
-
-  private void checkThemeIsAvailable(final String name, final Theme theme) {
-    if (theme == null) {
-      final String error = "Theme not found! name: '" + name + "'. "
-          + "Please ensure you have a tobago-config.xml with a theme-definition in your "
-          + "theme JAR. Found the following themes: " + availableThemes.keySet();
-      LOG.error(error);
-      throw new TobagoConfigurationException(error);
-    }
-  }
-
-  @Override
-  public Theme getTheme(final String name) {
-    if (name == null) {
-      LOG.debug("searching theme: null");
-      return defaultTheme;
-    }
-    if (defaultTheme != null && defaultTheme.getName().equals(name)) {
-      return defaultTheme;
-    }
-    for (final Theme theme : supportedThemes) {
-      if (theme.getName().equals(name)) {
-        return theme;
-      }
-    }
-    LOG.debug("searching theme '{}' not found. Using default: {}", name, defaultTheme);
-    return defaultTheme;
-  }
-
-  protected void setDefaultThemeName(final String defaultThemeName) {
-    checkUnlocked();
-    this.defaultThemeName = defaultThemeName;
-  }
-
-  @Override
-  public List<Theme> getSupportedThemes() {
-    return supportedThemes;
-  }
-
-  @Override
-  public Theme getDefaultTheme() {
-    return defaultTheme;
-  }
-
-  protected void addAvailableTheme(final ThemeImpl availableTheme) {
-    checkUnlocked();
-    final String name = availableTheme.getName();
-    if (availableThemes.containsKey(name)) {
-      final ThemeImpl base = availableThemes.get(name);
-      availableThemes.put(name, ThemeImpl.merge(base, availableTheme));
-    } else {
-      availableThemes.put(name, availableTheme);
-    }
-  }
-
-  public Map<String, ThemeImpl> getAvailableThemes() {
-    return availableThemes;
-  }
-
-  protected synchronized void initDefaultValidatorInfo() {
-    if (defaultValidatorInfo != null) {
-      checkUnlocked();
-    }
-    final FacesContext facesContext = FacesContext.getCurrentInstance();
-    if (facesContext != null) {
-      try {
-        final Application application = facesContext.getApplication();
-        final Map<String, String> map = application.getDefaultValidatorInfo();
-        if (map.size() > 0) {
-          defaultValidatorInfo = Collections.unmodifiableMap(map);
-        } else {
-          defaultValidatorInfo = Collections.emptyMap();
-        }
-      } catch (final Exception e) {
-        LOG.error("Can't initialize default validators (this happens with JBoss GateIn 3.6.0).", e);
-        defaultValidatorInfo = Collections.emptyMap();
-      }
-    }
-  }
-
-  @Override
-  public boolean isCreateSessionSecret() {
-    return createSessionSecret;
-  }
-
-  protected void setCreateSessionSecret(final boolean createSessionSecret) {
-    checkUnlocked();
-    this.createSessionSecret = createSessionSecret;
-  }
-
-  @Override
-  public boolean isCheckSessionSecret() {
-    return checkSessionSecret;
-  }
-
-  protected void setCheckSessionSecret(final boolean checkSessionSecret) {
-    checkUnlocked();
-    this.checkSessionSecret = checkSessionSecret;
-  }
-
-
-  @Override
-  public boolean isPreventFrameAttacks() {
-    return preventFrameAttacks;
-  }
-
-  protected void setPreventFrameAttacks(final boolean preventFrameAttacks) {
-    checkUnlocked();
-    this.preventFrameAttacks = preventFrameAttacks;
-  }
-
-  @Override
-  public ContentSecurityPolicy getContentSecurityPolicy() {
-    return contentSecurityPolicy;
-  }
-
-  @Override
-  public boolean isSetNosniffHeader() {
-    return setNosniffHeader;
-  }
-
-  protected void setSetNosniffHeader(final boolean setNosniffHeader) {
-    checkUnlocked();
-    this.setNosniffHeader = setNosniffHeader;
-  }
-
-  @Override
-  public SecurityAnnotation getSecurityAnnotation() {
-    return securityAnnotation;
-  }
-
-  public void setSecurityAnnotation(final SecurityAnnotation securityAnnotation) {
-    checkUnlocked();
-    this.securityAnnotation = securityAnnotation;
-  }
-
-  public Map<String, String> getDefaultValidatorInfo() {
-    // TODO: if the startup hasn't found a FacesContext and Application, this may depend on the order of the listeners.
-    if (defaultValidatorInfo == null) {
-      initDefaultValidatorInfo();
-    }
-    return defaultValidatorInfo;
-  }
-
-  @Override
-  public Sanitizer getSanitizer() {
-    return sanitizer;
-  }
-
-  protected void setSanitizer(final Sanitizer sanitizer) {
-    checkUnlocked();
-    this.sanitizer = sanitizer;
-  }
-
-  @Override
-  public boolean isDecodeLineFeed() {
-    return decodeLineFeed;
-  }
-
-  public void setDecodeLineFeed(final boolean decodeLineFeed) {
-    checkUnlocked();
-    this.decodeLineFeed = decodeLineFeed;
-  }
-
-  @Override
-  public Map<String, String> getMimeTypes() {
-    return mimeTypes;
-  }
-
-  @Override
-  public String toString() {
-    final StringBuilder builder = new StringBuilder();
-    builder.append("TobagoConfigImpl{");
-    builder.append("\nsupportedThemes=[");
-    for (final Theme supportedTheme : supportedThemes) {
-      builder.append(supportedTheme.getName());
-      builder.append(", ");
-    }
-    builder.append("], \ndefaultTheme=");
-    builder.append(defaultTheme != null ? defaultTheme.getName() : null);
-    builder.append(", \navailableThemes=");
-    builder.append(availableThemes.keySet());
-    builder.append(", \ncreateSessionSecret=");
-    builder.append(createSessionSecret);
-    builder.append(", \ncheckSessionSecret=");
-    builder.append(checkSessionSecret);
-    builder.append(", \npreventFrameAttacks=");
-    builder.append(preventFrameAttacks);
-    builder.append(", \ncontentSecurityPolicy=");
-    builder.append(contentSecurityPolicy);
-    builder.append(", \nsecurityAnnotation=");
-    builder.append(securityAnnotation);
-    builder.append(", \nsetNosniffHeader=");
-    builder.append(setNosniffHeader);
-    builder.append(", \ndefaultValidatorInfo=");
-    builder.append(defaultValidatorInfo);
-    builder.append(", \nsanitizer=");
-    builder.append(sanitizer);
-    builder.append(", \ndecodeLineFeed=");
-    builder.append(decodeLineFeed);
-    // to see only different (ignore alternative names for the same theme)
-    builder.append(", \nthemes=");
-    final Set<Theme> all = new HashSet<>(availableThemes.values());
-    builder.append(all);
-    builder.append(", \nmimeTypes=");
-    builder.append(mimeTypes);
-    builder.append('}');
-    return builder.toString();
-  }
-}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/config/TobagoConfigLoader.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/config/TobagoConfigLoader.java
new file mode 100644
index 0000000..7291fa2
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/config/TobagoConfigLoader.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.myfaces.tobago.internal.config;
+
+import org.apache.myfaces.tobago.internal.util.ArrayUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.SAXException;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.List;
+
+public class TobagoConfigLoader {
+
+  private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+  private static final String WEB_INF_TOBAGO_CONFIG_XML = "/WEB-INF/tobago-config.xml";
+  private static final String[] META_INF_TOBAGO_CONFIG_XML = {"META-INF/tobago-config.xml"};
+
+  private TobagoConfigLoader() {
+  }
+
+  public static void load(
+      final List<TobagoConfigFragment> fragments, final ServletContext servletContext, final String... alternative)
+      throws URISyntaxException, SAXException, ParserConfigurationException, IOException, ServletException {
+
+    TobagoConfigLoader loader = new TobagoConfigLoader();
+    loader.loadFromWebInf(fragments, servletContext);
+    loader.loadFromClasspath(fragments, alternative);
+  }
+
+  private void loadFromWebInf(
+      final List<TobagoConfigFragment> configFragmentList, final ServletContext servletContext)
+      throws IOException, SAXException, ParserConfigurationException, URISyntaxException {
+
+    if (servletContext != null) {
+      final URL url = servletContext.getResource(WEB_INF_TOBAGO_CONFIG_XML);
+      if (url != null) {
+        configFragmentList.add(new TobagoConfigParser().parse(url));
+      }
+    } else {
+      LOG.warn("No ServletContext to look for files in {}", WEB_INF_TOBAGO_CONFIG_XML);
+    }
+  }
+
+  private void loadFromClasspath(final List<TobagoConfigFragment> configFragmentList, final String... alternative)
+      throws ServletException {
+
+    final String[] configFiles;
+    if (ArrayUtils.isEmpty(alternative)) {
+      configFiles = META_INF_TOBAGO_CONFIG_XML;
+    } else {
+      configFiles = alternative;
+    }
+
+    try {
+      final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+      for (String configFile : configFiles) {
+        final Enumeration<URL> urls = classLoader.getResources(configFile);
+        while (urls.hasMoreElements()) {
+          final URL themeUrl = urls.nextElement();
+          try {
+            final TobagoConfigFragment fragment = new TobagoConfigParser().parse(themeUrl);
+            fragment.setUrl(themeUrl);
+            configFragmentList.add(fragment);
+
+            // tomcat uses jar
+            // weblogic uses zip
+            // IBM WebSphere uses wsjar
+            final String protocol = themeUrl.getProtocol();
+            if (!"file".equals(protocol) && !"jar".equals(protocol)
+                && !"zip".equals(protocol) && !"wsjar".equals(protocol)) {
+              LOG.warn("Unknown protocol '" + themeUrl + "'");
+            }
+          } catch (final Exception e) {
+            throw new Exception(e.getClass().getName() + " on themeUrl: " + themeUrl, e);
+          }
+        }
+      }
+    } catch (final Exception e) {
+      final String msg = "while loading ";
+      LOG.error(msg, e);
+      throw new ServletException(msg, e);
+    }
+  }
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/config/TobagoConfigMerger.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/config/TobagoConfigMerger.java
index e28e389..9182f40 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/config/TobagoConfigMerger.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/config/TobagoConfigMerger.java
@@ -19,7 +19,10 @@
 
 package org.apache.myfaces.tobago.internal.config;
 
+import org.apache.myfaces.tobago.config.TobagoConfig;
+import org.apache.myfaces.tobago.context.Theme;
 import org.apache.myfaces.tobago.context.ThemeImpl;
+import org.apache.myfaces.tobago.exception.TobagoConfigurationException;
 import org.apache.myfaces.tobago.sanitizer.IgnoringSanitizer;
 import org.apache.myfaces.tobago.sanitizer.JsoupSanitizer;
 import org.apache.myfaces.tobago.sanitizer.Sanitizer;
@@ -35,56 +38,62 @@ public class TobagoConfigMerger {
 
   private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
-  private final List<TobagoConfigFragment> list;
+  private final List<TobagoConfigFragment> fragments;
+  private final TobagoConfig tobagoConfig;
 
-  public TobagoConfigMerger(final List<TobagoConfigFragment> list) {
-    this.list =    list;
+  private TobagoConfigMerger(final List<TobagoConfigFragment> fragments, final TobagoConfig tobagoConfig) {
+    this.fragments = fragments;
+    this.tobagoConfig = tobagoConfig;
   }
 
-  public TobagoConfigImpl merge() {
+  public static void merge(final List<TobagoConfigFragment> fragments, final TobagoConfig tobagoConfig) {
+    final TobagoConfigMerger merger = new TobagoConfigMerger(fragments, tobagoConfig);
+    merger.merge();
+    merger.resolveThemes();
+  }
 
-    final TobagoConfigImpl result = new TobagoConfigImpl("fixme"); // fixme workaround
+  private void merge() {
 
     // default sanitizer
     String sanitizerClass = JsoupSanitizer.class.getName();
     Properties sanitizerProperties = new Properties();
     sanitizerProperties.setProperty("whitelist", "relaxed");
 
-    for (TobagoConfigFragment fragment : list) {
+    for (TobagoConfigFragment fragment : fragments) {
 
       // default theme
       final String defaultTheme = fragment.getDefaultThemeName();
       if (defaultTheme != null) {
-        result.setDefaultThemeName(defaultTheme);
+        tobagoConfig.setDefaultThemeName(defaultTheme);
       }
 
       // supported themes
       for (final String supported : fragment.getSupportedThemeNames()) {
-        result.addSupportedThemeName(supported);
+        tobagoConfig.addSupportedThemeName(supported);
       }
 
       // session secret
       if (fragment.getCreateSessionSecret() != null) {
-        result.setCreateSessionSecret(fragment.getCreateSessionSecret());
+        tobagoConfig.setCreateSessionSecret(fragment.getCreateSessionSecret());
       }
       if (fragment.getCheckSessionSecret() != null) {
-        result.setCheckSessionSecret(fragment.getCheckSessionSecret());
+        tobagoConfig.setCheckSessionSecret(fragment.getCheckSessionSecret());
       }
 
       if (fragment.getPreventFrameAttacks() != null) {
-        result.setPreventFrameAttacks(fragment.getPreventFrameAttacks());
+        tobagoConfig.setPreventFrameAttacks(fragment.getPreventFrameAttacks());
       }
 
       if (fragment.getContentSecurityPolicy() != null) {
-        result.getContentSecurityPolicy().merge(fragment.getContentSecurityPolicy());
+        tobagoConfig.getContentSecurityPolicy().merge(fragment.getContentSecurityPolicy());
       }
 
       if (fragment.getSecurityAnnotation() != null) {
-        result.setSecurityAnnotation(fragment.getSecurityAnnotation());
+        tobagoConfig.setSecurityAnnotation(fragment.getSecurityAnnotation());
       }
 
       if (fragment.getSetNosniffHeader() != null) {
-        result.setSetNosniffHeader(fragment.getSetNosniffHeader());
+        tobagoConfig.setSetNosniffHeader(fragment.getSetNosniffHeader());
       }
 
       if (fragment.getSanitizerClass() != null) {
@@ -93,39 +102,37 @@ public class TobagoConfigMerger {
       }
 
       if (fragment.getDecodeLineFeed() != null) {
-        result.setDecodeLineFeed(fragment.getDecodeLineFeed());
+        tobagoConfig.setDecodeLineFeed(fragment.getDecodeLineFeed());
       }
 
       // theme definition
       for (final ThemeImpl theme : fragment.getThemeDefinitions()) {
-        result.addAvailableTheme(theme);
+        tobagoConfig.addAvailableTheme(theme);
       }
 
       // url
       // todo???
 
-      final Map<String, String> mimeTypes = result.getMimeTypes();
+      final Map<String, String> mimeTypes = tobagoConfig.getMimeTypes();
       for (final Map.Entry<String, String> entry : fragment.getMimeTypes().entrySet()) {
         mimeTypes.put(entry.getKey(), entry.getValue());
       }
 
     }
 
-    resolveThemes(result.getAvailableThemes());
+    resolveThemes(tobagoConfig.getAvailableThemes());
 
     if (sanitizerClass != null) {
       try {
         final Class<? extends Sanitizer> aClass = Class.forName(sanitizerClass).asSubclass(Sanitizer.class);
         final Sanitizer sanitizer = aClass.newInstance();
         sanitizer.setProperties(sanitizerProperties);
-        result.setSanitizer(sanitizer);
+        tobagoConfig.setSanitizer(sanitizer);
       } catch (final Exception e) {
         LOG.error("Can't create sanitizer: '" + sanitizerClass + "'", e);
-        result.setSanitizer(new IgnoringSanitizer());
+        tobagoConfig.setSanitizer(new IgnoringSanitizer());
       }
     }
-
-    return result;
   }
 
   private void resolveThemes(final Map<String, ThemeImpl> map) {
@@ -145,4 +152,66 @@ public class TobagoConfigMerger {
     }
   }
 
+  private void resolveThemes() {
+
+    final Map<String, ThemeImpl> availableThemes = tobagoConfig.getAvailableThemes();
+
+    if (tobagoConfig.getDefaultThemeName() != null) {
+      final String defaultThemeName = tobagoConfig.getDefaultThemeName();
+      final ThemeImpl defaultTheme = availableThemes.get(defaultThemeName);
+      tobagoConfig.setDefaultTheme(defaultTheme);
+      checkThemeIsAvailable(defaultThemeName, defaultTheme, availableThemes);
+      if (LOG.isDebugEnabled()) {
+        LOG.debug("name = '{}'", defaultThemeName);
+        LOG.debug("defaultTheme = '{}'", defaultTheme);
+      }
+    } else {
+      int deep = 0;
+      Theme defaultTheme = null;
+      for (final Map.Entry<String, ThemeImpl> entry : availableThemes.entrySet()) {
+        final Theme theme = entry.getValue();
+        if (theme.getFallbackList().size() > deep) {
+          defaultTheme = theme;
+          deep = theme.getFallbackList().size();
+        }
+      }
+      if (defaultTheme == null) {
+        final String error = "Did not found any theme! "
+            + "Please ensure you have a tobago-config.xml with a theme-definition in your "
+            + "theme JAR. Please add a theme JAR to your classpath. Usually "
+            + "tobago-theme-standard.jar in WEB-INF/lib";
+        LOG.error(error);
+        throw new TobagoConfigurationException(error);
+      } else {
+        tobagoConfig.setDefaultTheme(defaultTheme);
+        if (LOG.isInfoEnabled()) {
+          LOG.info("Using default Theme {}", defaultTheme.getName());
+        }
+      }
+    }
+    if (!tobagoConfig.getSupportedThemeNames().isEmpty()) {
+      for (final String name : tobagoConfig.getSupportedThemeNames()) {
+        final Theme theme = availableThemes.get(name);
+        checkThemeIsAvailable(name, theme, availableThemes);
+        tobagoConfig.getSupportedThemes().add(theme);
+        if (LOG.isDebugEnabled()) {
+          LOG.debug("name = '{}'", name);
+          LOG.debug("last added theme = '{}'", theme);
+        }
+      }
+    }
+  }
+
+  private void checkThemeIsAvailable(
+      final String name, final Theme theme, final Map<String, ThemeImpl>availableThemes) {
+    if (theme == null) {
+      final String error = "Theme not found! name: '" + name + "'. "
+          + "Please ensure you have a tobago-config.xml with a theme-definition in your "
+          + "theme JAR. Found the following themes: " + availableThemes.keySet();
+      LOG.error(error);
+      throw new TobagoConfigurationException(error);
+    }
+  }
+
+
 }
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/config/TobagoConfigSorter.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/config/TobagoConfigSorter.java
index b84e3b4..8c1bc75 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/config/TobagoConfigSorter.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/config/TobagoConfigSorter.java
@@ -32,7 +32,14 @@ public class TobagoConfigSorter {
 
   private final List<Vertex> vertices = new ArrayList<>();
 
-  public TobagoConfigSorter(final List<TobagoConfigFragment> fragmentList) {
+  public static void sort(List<TobagoConfigFragment> fragments) {
+    TobagoConfigSorter sorter = new TobagoConfigSorter(fragments);
+    final List<TobagoConfigFragment> sorted = sorter.topologicalSort();
+    fragments.clear();
+    fragments.addAll(sorted);
+  }
+
+  private TobagoConfigSorter(final List<TobagoConfigFragment> fragmentList) {
     for (TobagoConfigFragment tobagoConfigFragment : fragmentList) {
       vertices.add(new Vertex(tobagoConfigFragment));
     }
@@ -43,7 +50,7 @@ public class TobagoConfigSorter {
    *
    * @throws IllegalStateException When detecting a cycle.
    */
-  public List<TobagoConfigFragment> topologicalSort() {
+  private List<TobagoConfigFragment> topologicalSort() {
 
     createEdges();
     checkCycles();
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/PageRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/PageRenderer.java
index d5ed108..e641318 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/PageRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/PageRenderer.java
@@ -54,7 +54,6 @@ import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.enterprise.inject.spi.CDI;
 import javax.faces.application.Application;
 import javax.faces.application.ProjectStage;
 import javax.faces.application.ViewHandler;
@@ -105,8 +104,8 @@ public class PageRenderer<T extends AbstractUIPage> extends RendererBase<T> {
   @Override
   public void encodeBeginInternal(final FacesContext facesContext, final T component) throws IOException {
 
-    final TobagoConfig tobagoConfig = CDI.current().select(TobagoConfig.class).get(); // todo: may inject
-    final TobagoContext tobagoContext = CDI.current().select(TobagoContext.class).get(); // todo: may inject
+    final TobagoConfig tobagoConfig = TobagoConfig.getInstance(facesContext);
+    final TobagoContext tobagoContext = TobagoContext.getInstance(facesContext);
 
     if (tobagoContext.getFocusId() == null && !StringUtils.isBlank(component.getFocusId())) {
       tobagoContext.setFocusId(component.getFocusId());
@@ -290,13 +289,12 @@ public class PageRenderer<T extends AbstractUIPage> extends RendererBase<T> {
     writer.endElement(HtmlElements.TOBAGO_FOCUS);
 
     if (tobagoConfig.isCheckSessionSecret()) {
+      final Secret secret = Secret.getInstance(facesContext);
       writer.startElement(HtmlElements.INPUT);
       writer.writeAttribute(HtmlAttributes.TYPE, HtmlInputTypes.HIDDEN);
       writer.writeAttribute(HtmlAttributes.NAME, Secret.KEY, false);
       writer.writeAttribute(HtmlAttributes.ID, Secret.KEY, false);
-//      final Object session = facesContext.getExternalContext().getSession(true);
-      final Secret secret = CDI.current().select(Secret.class).get();
-      secret.encode(writer);
+      writer.writeAttribute(HtmlAttributes.VALUE, secret.getSecret(), false);
       writer.endElement(HtmlElements.INPUT);
     }
 
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/ArrayUtils.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/ArrayUtils.java
index 5bd40f3..4202b42 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/ArrayUtils.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/ArrayUtils.java
@@ -47,4 +47,7 @@ public class ArrayUtils {
     return false;
   }
 
+  public static boolean isEmpty(final String[] array) {
+    return array == null || array.length == 0;
+  }
 }
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/AuthorizationHelper.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/AuthorizationHelper.java
index 678b3ab..87a3318 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/AuthorizationHelper.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/AuthorizationHelper.java
@@ -25,10 +25,9 @@ import org.slf4j.LoggerFactory;
 import javax.annotation.security.DenyAll;
 import javax.annotation.security.PermitAll;
 import javax.annotation.security.RolesAllowed;
+import javax.el.ELContext;
 import javax.enterprise.context.ApplicationScoped;
 import javax.enterprise.inject.spi.Bean;
-import javax.enterprise.inject.spi.BeanManager;
-import javax.enterprise.inject.spi.CDI;
 import javax.faces.context.FacesContext;
 import javax.inject.Named;
 import java.lang.annotation.Annotation;
@@ -67,33 +66,15 @@ public class AuthorizationHelper {
 
   private final Map<String, Object> cache = new ConcurrentHashMap<>();
 
-  private final BeanManager beanManager;
-
-  public AuthorizationHelper() {
-    beanManager = CDI.current().getBeanManager();
-    LOG.info("Using bean manager: '{}'", beanManager);
-  }
-
   public static AuthorizationHelper getInstance(final FacesContext facesContext) {
+    final ELContext elContext = facesContext.getELContext();
     return (AuthorizationHelper)
-        facesContext.getELContext().getELResolver().getValue(facesContext.getELContext(), null, AUTHORIZATION_HELPER);
-  }
-
-  Object getObject(String beanString) {
-    Object bean = null;
-    for (final Bean<?> entry : beanManager.getBeans(beanString)) {
-      if (bean == null) {
-        bean = entry;
-      } else {
-        LOG.warn("Bean name ambiguous: '{}'", beanString);
-      }
-    }
-    return bean;
+        elContext.getELResolver().getValue(elContext, null, AUTHORIZATION_HELPER);
   }
 
   public boolean isAuthorized(final FacesContext facesContext, final String expression) {
 
-    final Annotation securityAnnotation = getSecurityAnnotation(expression);
+    final Annotation securityAnnotation = getSecurityAnnotation(facesContext, expression);
     if (securityAnnotation == null) {
       return true;
     }
@@ -126,7 +107,7 @@ public class AuthorizationHelper {
     return true;
   }
 
-  private Annotation getSecurityAnnotation(final String expression) {
+  private Annotation getSecurityAnnotation(final FacesContext facesContext, final String expression) {
     if (cache.containsKey(expression)) {
       final Object obj = cache.get(expression);
       if (obj instanceof Annotation) {
@@ -140,7 +121,9 @@ public class AuthorizationHelper {
         final String beanString = matcher.group(1);
         final String methodString = matcher.group(2);
 
-        final Object bean = getObject(beanString);
+        final ELContext elContext = facesContext.getELContext();
+        final Object bean = elContext
+            .getELResolver().getValue(elContext, null, beanString);
         if (bean != null) {
           // try first from method
           final List<Method> methods = findMethods(bean, methodString);
@@ -188,7 +171,13 @@ public class AuthorizationHelper {
   }
 
   private List<Method> findMethods(final Object bean, final String name) {
-    final Class clazz = ((Bean) bean).getBeanClass();
+    final Class clazz;
+    if (bean instanceof Bean) {
+      clazz = ((Bean) bean).getBeanClass();
+    } else {
+      // XXX check if this works correctly with spring.
+      clazz = bean.getClass();
+    }
     final Method[] methods = clazz.getMethods();
     final List<Method> result = new ArrayList<>();
     for (final Method method : methods) {
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/lifecycle/SecretPhaseListener.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/lifecycle/SecretPhaseListener.java
index 1d6d9ae..019308f 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/lifecycle/SecretPhaseListener.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/lifecycle/SecretPhaseListener.java
@@ -24,7 +24,6 @@ import org.apache.myfaces.tobago.webapp.Secret;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.enterprise.inject.spi.CDI;
 import javax.faces.context.FacesContext;
 import javax.faces.event.PhaseEvent;
 import javax.faces.event.PhaseId;
@@ -39,7 +38,7 @@ public class SecretPhaseListener implements PhaseListener {
   @Override
   public void afterPhase(final PhaseEvent event) {
     final FacesContext facesContext = event.getFacesContext();
-    final TobagoConfig tobagoConfig = CDI.current().select(TobagoConfig.class).get();
+    final TobagoConfig tobagoConfig = TobagoConfig.getInstance(facesContext);
 
     if (!facesContext.getResponseComplete()
         && facesContext.isPostback()
@@ -58,9 +57,9 @@ public class SecretPhaseListener implements PhaseListener {
    */
   private boolean check(final FacesContext facesContext) {
     final Map<String, String> requestParameterMap = facesContext.getExternalContext().getRequestParameterMap();
-    final String fromRequest = requestParameterMap.get(Secret.KEY);
-    final Secret secret = CDI.current().select(Secret.class).get();
-    return secret.check(fromRequest);
+    final String secretFromRequest = requestParameterMap.get(Secret.KEY);
+    final Secret secret = Secret.getInstance(facesContext);
+    return secret.check(secretFromRequest);
   }
 
   @Override
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/webapp/Secret.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/webapp/Secret.java
index 995a4a7..dba57c0 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/webapp/Secret.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/webapp/Secret.java
@@ -20,16 +20,12 @@
 package org.apache.myfaces.tobago.webapp;
 
 import org.apache.myfaces.tobago.internal.util.RandomUtils;
-import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
 
-import javax.enterprise.context.SessionScoped;
-import javax.inject.Named;
-import java.io.IOException;
+import javax.faces.context.FacesContext;
+import javax.servlet.http.HttpSession;
 import java.io.Serializable;
 
-@Named
-@SessionScoped
-public class Secret implements Serializable {
+public final class Secret implements Serializable {
 
   private static final long serialVersionUID = 1L;
 
@@ -41,11 +37,23 @@ public class Secret implements Serializable {
     secret = RandomUtils.nextString();
   }
 
+  public static Secret getInstance(final FacesContext facesContext) {
+    return (Secret) facesContext.getExternalContext().getSessionMap().get(Secret.KEY);
+  }
+
+  /**
+   * Create a secret attribute in the session.
+   * Should usually be called in a {@link javax.servlet.http.HttpSessionListener}.
+   */
+  public static void create(final HttpSession session) {
+    session.setAttribute(Secret.KEY, new Secret());
+  }
+
   public boolean check(final String test) {
     return secret.equals(test);
   }
 
-  public void encode(TobagoResponseWriter writer) throws IOException {
-    writer.writeAttribute(HtmlAttributes.VALUE, this.secret, false);
+  public String getSecret() {
+    return secret;
   }
 }
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/ArrayUtils.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/webapp/SecretSessionListener.java
similarity index 55%
copy from tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/ArrayUtils.java
copy to tobago-core/src/main/java/org/apache/myfaces/tobago/webapp/SecretSessionListener.java
index 5bd40f3..79d4b0d 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/ArrayUtils.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/webapp/SecretSessionListener.java
@@ -17,34 +17,23 @@
  * under the License.
  */
 
-package org.apache.myfaces.tobago.internal.util;
+package org.apache.myfaces.tobago.webapp;
 
-public class ArrayUtils {
+import org.apache.myfaces.tobago.config.TobagoConfig;
 
-  public static final String[] EMPTY_STRING_ARRAY = new String[0];
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
 
-  public static <T> boolean contains(final T[] list, final T value) {
-    if (list == null) {
-      return false;
-    }
-    for (final T element : list) {
-      if (element == value || element != null && element.equals(value)) {
-        return true;
-      }
-    }
-    return false;
-  }
+public class SecretSessionListener implements HttpSessionListener {
 
-  public static boolean contains(final int[] list, final int value) {
-    if (list == null) {
-      return false;
+  @Override
+  public void sessionCreated(final HttpSessionEvent sessionEvent) {
+    if (TobagoConfig.getInstance(sessionEvent.getSession().getServletContext()).isCreateSessionSecret()) {
+      Secret.create(sessionEvent.getSession());
     }
-    for (final int element : list) {
-      if (element == value) {
-        return true;
-      }
-    }
-    return false;
   }
 
+  @Override
+  public void sessionDestroyed(final HttpSessionEvent sessionEvent) {
+  }
 }
diff --git a/tobago-core/src/main/resources/META-INF/web-fragment.xml b/tobago-core/src/main/resources/META-INF/web-fragment.xml
index 2ac0ab5..0543a4d 100644
--- a/tobago-core/src/main/resources/META-INF/web-fragment.xml
+++ b/tobago-core/src/main/resources/META-INF/web-fragment.xml
@@ -29,5 +29,8 @@
   <listener>
     <listener-class>org.apache.myfaces.tobago.webapp.TobagoServletContextListener</listener-class>
   </listener>
+  <listener>
+    <listener-class>org.apache.myfaces.tobago.webapp.SecretSessionListener</listener-class>
+  </listener>
 
 </web-fragment>
diff --git a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/AbstractTobagoTestBase.java b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/AbstractTobagoTestBase.java
index 90919d1..76518b9 100644
--- a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/AbstractTobagoTestBase.java
+++ b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/AbstractTobagoTestBase.java
@@ -48,6 +48,7 @@ import org.apache.myfaces.tobago.component.UISelectOneRadio;
 import org.apache.myfaces.tobago.component.UISeparator;
 import org.apache.myfaces.tobago.component.UIStyle;
 import org.apache.myfaces.tobago.component.UITextarea;
+import org.apache.myfaces.tobago.config.TobagoConfig;
 import org.apache.myfaces.tobago.context.TobagoContext;
 import org.apache.myfaces.tobago.internal.behavior.EventBehavior;
 import org.apache.myfaces.tobago.internal.renderkit.renderer.BadgeRenderer;
@@ -79,6 +80,7 @@ import org.junit.jupiter.api.BeforeEach;
 
 import javax.faces.component.behavior.AjaxBehavior;
 import javax.faces.render.RenderKit;
+import javax.servlet.ServletContext;
 import java.io.IOException;
 import java.io.StringWriter;
 import java.nio.charset.StandardCharsets;
@@ -115,8 +117,8 @@ public abstract class AbstractTobagoTestBase extends AbstractJsfTestCase {
     getFacesContext().setResponseWriter(new HtmlResponseWriter(stringWriter, "", StandardCharsets.UTF_8));
 
     // Tobago specific extensions
-    final TobagoConfigImpl tobagoConfig = TobagoConfigMergingUnitTest.load("tobago-config-for-unit-tests.xml");
-    tobagoConfig.initDefaultValidatorInfo();
+    final TobagoConfig tobagoConfig = new TobagoConfig(
+        (ServletContext) facesContext.getExternalContext().getContext(), "tobago-config-for-unit-tests.xml");
     servletContext.setAttribute(TOBAGO_CONFIG, tobagoConfig);
     facesContext.getExternalContext().getApplicationMap().put(TOBAGO_CONFIG, tobagoConfig);
 
diff --git a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/TobagoConfigMergingUnitTest.java b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/TobagoConfigMergingUnitTest.java
index 5c7bf8e..b4c1c94 100644
--- a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/TobagoConfigMergingUnitTest.java
+++ b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/TobagoConfigMergingUnitTest.java
@@ -19,57 +19,45 @@
 
 package org.apache.myfaces.tobago.internal.config;
 
+import org.apache.myfaces.tobago.config.TobagoConfig;
 import org.apache.myfaces.tobago.context.Theme;
 import org.apache.myfaces.tobago.context.ThemeScript;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
-import org.xml.sax.SAXException;
-
-import javax.xml.parsers.ParserConfigurationException;
-import java.io.IOException;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.List;
+
 import java.util.Map;
 
 public class TobagoConfigMergingUnitTest {
 
   @Test
-  public void testPreventFrameAttacksCascadingDefault()
-      throws IOException, SAXException, ParserConfigurationException, URISyntaxException {
+  public void testPreventFrameAttacksCascadingDefault() {
 
-    final TobagoConfigImpl config = load(
-        "tobago-config-merge-0.xml",
-        "tobago-config-merge-1.xml");
+    final TobagoConfig config =
+        new TobagoConfig(null, "tobago-config-merge-0.xml", "tobago-config-merge-1.xml");
 
     Assertions.assertFalse(config.isPreventFrameAttacks());
   }
 
   @Test
-  public void testPreventFrameAttacks()
-      throws IOException, SAXException, ParserConfigurationException, URISyntaxException {
+  public void testPreventFrameAttacks() {
 
-    final TobagoConfigImpl config = load("tobago-config-merge-0.xml");
+    final TobagoConfig config = new TobagoConfig(null, "tobago-config-merge-0.xml");
 
     Assertions.assertFalse(config.isPreventFrameAttacks());
   }
 
   @Test
-  public void testPreventFrameAttacksDefault()
-      throws IOException, SAXException, ParserConfigurationException, URISyntaxException {
+  public void testPreventFrameAttacksDefault() {
 
-    final TobagoConfigImpl config = load("tobago-config-merge-1.xml");
+    final TobagoConfig config = new TobagoConfig(null, "tobago-config-merge-1.xml");
 
     Assertions.assertTrue(config.isPreventFrameAttacks());
   }
 
   @Test
-  public void testContentSecurityPolicy()
-      throws IOException, SAXException, ParserConfigurationException, URISyntaxException {
+  public void testContentSecurityPolicy() {
 
-    final TobagoConfigImpl config = load(
-        "tobago-config-merge-0.xml");
+    final TobagoConfig config = new TobagoConfig(null, "tobago-config-merge-0.xml");
 
     Assertions.assertSame(config.getContentSecurityPolicy().getMode(), ContentSecurityPolicy.Mode.ON);
     final Map<String, String> directiveMap = config.getContentSecurityPolicy().getDirectiveMap();
@@ -78,12 +66,10 @@ public class TobagoConfigMergingUnitTest {
   }
 
   @Test
-  public void testContentSecurityPolicyExtend()
-      throws IOException, SAXException, ParserConfigurationException, URISyntaxException {
+  public void testContentSecurityPolicyExtend() {
 
-    final TobagoConfigImpl config = load(
-        "tobago-config-merge-0.xml",
-        "tobago-config-merge-1.xml");
+    final TobagoConfig config =
+        new TobagoConfig(null, "tobago-config-merge-0.xml", "tobago-config-merge-1.xml");
 
     Assertions.assertSame(config.getContentSecurityPolicy().getMode(), ContentSecurityPolicy.Mode.REPORT_ONLY);
     final Map<String, String> directiveMap = config.getContentSecurityPolicy().getDirectiveMap();
@@ -93,12 +79,9 @@ public class TobagoConfigMergingUnitTest {
   }
 
   @Test
-  public void testContentSecurityPolicyOff()
-      throws IOException, SAXException, ParserConfigurationException, URISyntaxException {
+  public void testContentSecurityPolicyOff() {
 
-    final TobagoConfigImpl config = load(
-        "tobago-config-merge-0.xml",
-        "tobago-config-merge-1.xml",
+    final TobagoConfig config = new TobagoConfig(null, "tobago-config-merge-0.xml", "tobago-config-merge-1.xml",
         "tobago-config-merge-2.xml");
 
     Assertions.assertSame(config.getContentSecurityPolicy().getMode(), ContentSecurityPolicy.Mode.OFF);
@@ -106,12 +89,10 @@ public class TobagoConfigMergingUnitTest {
   }
 
   @Test
-  public void testContentSecurityPolicyNameAttribute()
-      throws IOException, SAXException, ParserConfigurationException, URISyntaxException {
+  public void testContentSecurityPolicyNameAttribute() {
 
-    final TobagoConfigImpl config = load(
-        "tobago-config-merge-0.xml",
-        "tobago-config-merge-3.xml");
+    final TobagoConfig config =
+        new TobagoConfig(null, "tobago-config-merge-0.xml", "tobago-config-merge-3.xml");
 
     Assertions.assertSame(config.getContentSecurityPolicy().getMode(), ContentSecurityPolicy.Mode.ON);
     final Map<String, String> directiveMap = config.getContentSecurityPolicy().getDirectiveMap();
@@ -120,12 +101,9 @@ public class TobagoConfigMergingUnitTest {
   }
 
   @Test
-  public void testMimeTypes()
-      throws IOException, SAXException, ParserConfigurationException, URISyntaxException {
+  public void testMimeTypes() {
 
-    final TobagoConfigImpl config = load(
-        "tobago-config-merge-0.xml",
-        "tobago-config-merge-1.xml",
+    final TobagoConfig config = new TobagoConfig(null, "tobago-config-merge-0.xml", "tobago-config-merge-1.xml",
         "tobago-config-merge-2.xml");
 
     final Map<String, String> mimeTypes = config.getMimeTypes();
@@ -136,11 +114,10 @@ public class TobagoConfigMergingUnitTest {
   }
 
   @Test
-  public void testResourcePriority()
-      throws IOException, SAXException, ParserConfigurationException, URISyntaxException {
+  public void testResourcePriority() {
 
-    final TobagoConfigImpl config = load(
-        "tobago-config-5.0.xml", "tobago-config-5.0-replace.xml");
+    final TobagoConfig config =
+        new TobagoConfig(null, "tobago-config-5.0.xml", "tobago-config-5.0-replace.xml");
 
     final String[] expected = new String[]{
         "script-1.js",
@@ -171,12 +148,10 @@ public class TobagoConfigMergingUnitTest {
   }
 
   @Test
-  public void testMergeThemePatch()
-      throws IOException, SAXException, ParserConfigurationException, URISyntaxException {
+  public void testMergeThemePatch() {
 
-    final TobagoConfigImpl config12 = load(
-        "tobago-config-theme-merge-1.xml",
-        "tobago-config-theme-merge-2.xml");
+    final TobagoConfig config12 =
+        new TobagoConfig(null, "tobago-config-theme-merge-1.xml", "tobago-config-theme-merge-2.xml");
     Assertions.assertEquals("2.0", config12.getDefaultTheme().getVersion());
 
     final ThemeScript[] scripts12 = config12.getDefaultTheme().getScriptResources(false);
@@ -184,9 +159,8 @@ public class TobagoConfigMergingUnitTest {
     Assertions.assertEquals("script-1", scripts12[0].getName());
     Assertions.assertEquals("script-2", scripts12[1].getName());
 
-    final TobagoConfigImpl config21 = load(
-        "tobago-config-theme-merge-2.xml",
-        "tobago-config-theme-merge-1.xml");
+    final TobagoConfig config21 =
+        new TobagoConfig(null, "tobago-config-theme-merge-2.xml", "tobago-config-theme-merge-1.xml");
     Assertions.assertEquals("2.0", config21.getDefaultTheme().getVersion());
 
     final ThemeScript[] scripts21 = config21.getDefaultTheme().getScriptResources(false);
@@ -195,22 +169,4 @@ public class TobagoConfigMergingUnitTest {
     Assertions.assertEquals("script-2", scripts21[1].getName());
   }
 
-  public static TobagoConfigImpl load(final String... names)
-      throws IOException, SAXException, ParserConfigurationException, URISyntaxException {
-
-    final List<TobagoConfigFragment> list = new ArrayList<>();
-
-    for (final String name : names) {
-      final URL url = TobagoConfigMergingUnitTest.class.getClassLoader().getResource(name);
-      final TobagoConfigParser parser = new TobagoConfigParser();
-      list.add(parser.parse(url));
-    }
-
-    final TobagoConfigSorter sorter = new TobagoConfigSorter(list);
-    final TobagoConfigMerger merger = new TobagoConfigMerger(sorter.topologicalSort());
-    final TobagoConfigImpl result = merger.merge();
-    result.resolveThemes();
-    result.lock();
-    return result;
-  }
 }
diff --git a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/TobagoConfigSorterUnitTest.java b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/TobagoConfigSorterUnitTest.java
index d71964a..b793db8 100644
--- a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/TobagoConfigSorterUnitTest.java
+++ b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/TobagoConfigSorterUnitTest.java
@@ -99,20 +99,19 @@ public class TobagoConfigSorterUnitTest {
     list.add(m);
     list.add(n);
 
-    final TobagoConfigSorter sorter = new TobagoConfigSorter(list);
-    final List<TobagoConfigFragment> result = sorter.topologicalSort();
-
-    Assertions.assertEquals(a, result.get(0));
-    Assertions.assertEquals(b, result.get(1));
-    Assertions.assertEquals(c, result.get(2));
-    Assertions.assertEquals(u1, result.get(3));
-    Assertions.assertEquals(u2, result.get(4));
-    Assertions.assertEquals(d, result.get(5));
-    Assertions.assertEquals(e, result.get(6));
-    Assertions.assertEquals(f, result.get(7));
-    Assertions.assertEquals(u3, result.get(8));
-    Assertions.assertEquals(m, result.get(9));
-    Assertions.assertEquals(n, result.get(10));
+    TobagoConfigSorter.sort(list);
+
+    Assertions.assertEquals(a, list.get(0));
+    Assertions.assertEquals(b, list.get(1));
+    Assertions.assertEquals(c, list.get(2));
+    Assertions.assertEquals(u1, list.get(3));
+    Assertions.assertEquals(u2, list.get(4));
+    Assertions.assertEquals(d, list.get(5));
+    Assertions.assertEquals(e, list.get(6));
+    Assertions.assertEquals(f, list.get(7));
+    Assertions.assertEquals(u3, list.get(8));
+    Assertions.assertEquals(m, list.get(9));
+    Assertions.assertEquals(n, list.get(10));
   }
 
   @Test
@@ -122,11 +121,9 @@ public class TobagoConfigSorterUnitTest {
 
     final List<TobagoConfigFragment> list = new ArrayList<>();
 
-    final TobagoConfigSorter sorter = new TobagoConfigSorter(list);
+    TobagoConfigSorter.sort(list);
 
-    final List<TobagoConfigFragment> result = sorter.topologicalSort();
-
-    Assertions.assertTrue(result.isEmpty());
+    Assertions.assertTrue(list.isEmpty());
   }
 
   @Test
@@ -142,10 +139,8 @@ public class TobagoConfigSorterUnitTest {
     final List<TobagoConfigFragment> list = new ArrayList<>();
     list.add(a);
 
-    final TobagoConfigSorter sorter = new TobagoConfigSorter(list);
-
     try {
-      sorter.topologicalSort();
+      TobagoConfigSorter.sort(list);
 
       Assertions.fail("Cycle was not detected!");
     } catch (final RuntimeException e) {
@@ -166,10 +161,8 @@ public class TobagoConfigSorterUnitTest {
     final List<TobagoConfigFragment> list = new ArrayList<>();
     list.add(a);
 
-    final TobagoConfigSorter sorter = new TobagoConfigSorter(list);
-
     try {
-      sorter.topologicalSort();
+      TobagoConfigSorter.sort(list);
 
       Assertions.fail("Cycle was not detected!");
     } catch (final RuntimeException e) {
@@ -196,10 +189,8 @@ public class TobagoConfigSorterUnitTest {
     list.add(a);
     list.add(b);
 
-    final TobagoConfigSorter sorter = new TobagoConfigSorter(list);
-
     try {
-      sorter.topologicalSort();
+      TobagoConfigSorter.sort(list);
 
       Assertions.fail("Cycle was not detected!");
     } catch (final RuntimeException e) {
@@ -227,9 +218,8 @@ public class TobagoConfigSorterUnitTest {
     list.add(a);
     list.add(b);
 
-    final TobagoConfigSorter sorter = new TobagoConfigSorter(list);
     try {
-      sorter.topologicalSort();
+      TobagoConfigSorter.sort(list);
 
       Assertions.fail("Cycle was not detected!");
     } catch (final RuntimeException e) {
@@ -261,10 +251,8 @@ public class TobagoConfigSorterUnitTest {
     list.add(b);
     list.add(c);
 
-    final TobagoConfigSorter sorter = new TobagoConfigSorter(list);
-
     try {
-      sorter.topologicalSort();
+      TobagoConfigSorter.sort(list);
 
       Assertions.fail("Cycle was not detected!");
     } catch (final RuntimeException e) {
@@ -320,15 +308,13 @@ public class TobagoConfigSorterUnitTest {
     list.add(standard);
     list.add(core);
 
-    final TobagoConfigSorter sorter = new TobagoConfigSorter(list);
-
-    final List<TobagoConfigFragment> result = sorter.topologicalSort();
+    TobagoConfigSorter.sort(list);
 
-    Assertions.assertEquals(core, result.get(0));
-    Assertions.assertEquals(standard, result.get(1));
-    Assertions.assertEquals(blank, result.get(6));
-    final int blankPos = result.indexOf(blank);
-    final int speysidePos = result.indexOf(speyside);
+    Assertions.assertEquals(core, list.get(0));
+    Assertions.assertEquals(standard, list.get(1));
+    Assertions.assertEquals(blank, list.get(6));
+    final int blankPos = list.indexOf(blank);
+    final int speysidePos = list.indexOf(speyside);
     Assertions.assertTrue(blankPos > speysidePos);
   }
 }