You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by dk...@apache.org on 2018/11/07 05:45:26 UTC

[sling-whiteboard] branch master updated (0cbf6bf -> 9c90471)

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

dklco pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git.


    from 0cbf6bf  Enable api-regions runtime support via a framework property
     new 98fa48e  Added a faculty to log out the configs
     new 9c90471  Added code to read configurations and repoinits

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../sling-org-apache-sling-scripting-sightly       |   1 -
 upgrade/pom.xml                                    |  20 +++
 .../java/org/apache/sling/upgrade/BundleEntry.java |  51 +++---
 .../java/org/apache/sling/upgrade/ConfigEntry.java |  39 +++--
 ...upBundleEntry.java => EntryHandlerFactory.java} |  34 ++--
 .../org/apache/sling/upgrade/RepoInitEntry.java    |  74 +++++++++
 .../apache/sling/upgrade/StartupBundleEntry.java   |   6 -
 .../{UpgradeService.java => UpgradeEntry.java}     |  30 ++--
 .../org/apache/sling/upgrade/UpgradeRequest.java   |  26 +---
 .../sling/upgrade/impl/BundleEntryFactory.java     |  64 ++++++++
 .../ConfigEntryFactory.java}                       |  24 +--
 .../RepoinitEntryFactory.java}                     |  33 ++--
 .../StartupBundleEntryFactory.java}                |  30 ++--
 .../sling/upgrade/impl/UpgradeServiceImpl.java     |  32 ++--
 .../sling/upgrade/impl/UpgradeWebConsole.java      | 173 ++++++++++++++++-----
 upgrade/src/main/resources/bundleentry.html        |  11 ++
 upgrade/src/main/resources/bundles.html            |  19 +++
 upgrade/src/main/resources/config.html             |  18 +++
 upgrade/src/main/resources/configentry.html        |  10 ++
 upgrade/src/main/resources/form.html               |  35 +++++
 upgrade/src/main/resources/header.html             |  14 ++
 upgrade/src/main/resources/repoinit.html           |  16 ++
 upgrade/src/main/resources/repoinitentry.html      |  15 ++
 .../apache/sling/upgrade/UpgradeServiceTest.java   |  39 ++++-
 24 files changed, 620 insertions(+), 194 deletions(-)
 delete mode 160000 scripting-resolver/sling-org-apache-sling-scripting-sightly
 copy upgrade/src/main/java/org/apache/sling/upgrade/{StartupBundleEntry.java => EntryHandlerFactory.java} (53%)
 create mode 100644 upgrade/src/main/java/org/apache/sling/upgrade/RepoInitEntry.java
 copy upgrade/src/main/java/org/apache/sling/upgrade/{UpgradeService.java => UpgradeEntry.java} (61%)
 create mode 100644 upgrade/src/main/java/org/apache/sling/upgrade/impl/BundleEntryFactory.java
 copy upgrade/src/main/java/org/apache/sling/upgrade/{StartupBundleEntry.java => impl/ConfigEntryFactory.java} (64%)
 copy upgrade/src/main/java/org/apache/sling/upgrade/{StartupBundleEntry.java => impl/RepoinitEntryFactory.java} (52%)
 copy upgrade/src/main/java/org/apache/sling/upgrade/{StartupBundleEntry.java => impl/StartupBundleEntryFactory.java} (57%)
 create mode 100644 upgrade/src/main/resources/bundleentry.html
 create mode 100644 upgrade/src/main/resources/bundles.html
 create mode 100644 upgrade/src/main/resources/config.html
 create mode 100644 upgrade/src/main/resources/configentry.html
 create mode 100644 upgrade/src/main/resources/form.html
 create mode 100644 upgrade/src/main/resources/header.html
 create mode 100644 upgrade/src/main/resources/repoinit.html
 create mode 100644 upgrade/src/main/resources/repoinitentry.html


[sling-whiteboard] 01/02: Added a faculty to log out the configs

Posted by dk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dklco pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git

commit 98fa48e3e4ea429b96762b8e1b524b79785d733a
Author: Dan Klco <dk...@apache.org>
AuthorDate: Tue Nov 6 13:06:35 2018 -0500

    Added a faculty to log out the configs
---
 .../sling/upgrade/impl/UpgradeWebConsole.java      | 100 ++++++++++++++-------
 1 file changed, 67 insertions(+), 33 deletions(-)

diff --git a/upgrade/src/main/java/org/apache/sling/upgrade/impl/UpgradeWebConsole.java b/upgrade/src/main/java/org/apache/sling/upgrade/impl/UpgradeWebConsole.java
index 4022436..71b74c8 100644
--- a/upgrade/src/main/java/org/apache/sling/upgrade/impl/UpgradeWebConsole.java
+++ b/upgrade/src/main/java/org/apache/sling/upgrade/impl/UpgradeWebConsole.java
@@ -37,6 +37,7 @@ import org.apache.commons.fileupload.disk.DiskFileItemFactory;
 import org.apache.commons.fileupload.servlet.ServletFileUpload;
 import org.apache.felix.webconsole.AbstractWebConsolePlugin;
 import org.apache.sling.upgrade.BundleEntry;
+import org.apache.sling.upgrade.ConfigEntry;
 import org.apache.sling.upgrade.UpgradeRequest;
 import org.apache.sling.upgrade.UpgradeService;
 import org.osgi.service.component.annotations.Component;
@@ -53,15 +54,15 @@ import org.slf4j.LoggerFactory;
         "felix.webconsole.title=Upgrade", "felix.webconsole.category=Sling" })
 public class UpgradeWebConsole extends AbstractWebConsolePlugin {
 
+    static final String APP_ROOT = "upgrade";
+
+    private static final Logger log = LoggerFactory.getLogger(UpgradeWebConsole.class);
+
     /**
      * 
      */
     private static final long serialVersionUID = 2255746886735460607L;
 
-    static final String APP_ROOT = "upgrade";
-
-    private static final Logger log = LoggerFactory.getLogger(UpgradeWebConsole.class);
-
     @Reference
     private UpgradeService upgradeService;
 
@@ -118,25 +119,6 @@ public class UpgradeWebConsole extends AbstractWebConsolePlugin {
         }
     }
 
-    private void writeUpgradeInfo(HttpServletResponse resp, InputStream jarIs) throws IOException {
-        Writer out = resp.getWriter();
-        UpgradeRequest request = upgradeService.readSlingJar(jarIs);
-
-        out.write("<h2>" + request.getTitle() + " version " + request.getVersion());
-        out.write("<br/><small>" + request.getVendor() + "</small></h2>");
-
-        out.write("<h3>Startup Bundles</h3><ul>");
-        request.getStartupBundles().forEach(e -> {
-            writeBundle(e, out);
-        });
-
-        out.write("</ul><h3>Install Bundles</h3><ul>");
-        request.getBundles().forEach(e -> {
-            writeBundle(e, out);
-        });
-        out.write("</ul>");
-    }
-
     /*
      * (non-Javadoc)
      *
@@ -144,7 +126,7 @@ public class UpgradeWebConsole extends AbstractWebConsolePlugin {
      */
     @Override
     public String getLabel() {
-        return "fsclassloader";
+        return "upgrade";
     }
 
     /*
@@ -180,28 +162,80 @@ public class UpgradeWebConsole extends AbstractWebConsolePlugin {
     @Override
     protected void renderContent(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
         Writer out = res.getWriter();
-        out.write("<h1>Update Sling</h1>");
+
+        out.write("<table class=\"content\" cellspacing=\"0\" width=\"100%\" cellpadding=\"0\">");
+        out.write("<thead><tr><th class=\"content container\">Update Apache Sling</th></tr></thead>");
+        out.write("<tbody><tr><td  class=\"content\">");
+        out.write("<form method=\"post\" enctype=\"multipart/form-data\">");
+        out.write(
+                "<div><label for=\"jar\">Sling Jar</lable><br/><input type=\"file\" name=\"jar\" accepts=\"application/java-archive\"></div>");
         out.write(
-                "<form method=\"post\" enctype=\"multipart/form-data\"><label for=\"jar\">Update Jar File</lable><br/><input type=\"file\" name=\"jar\" accepts=\"application/java-archive\"><input type=\"submit\" /></form>");
+                "<div><label for=\"action\">Action</lable><br/><select name=\"action\"><option>Upgrade</option><option>Preview</option></div>");
+        out.write("<div><input type=\"submit\" value=\"Upload\" /></div>");
+        out.write("</form></td></tr></tbody></table>");
 
     }
 
     private void writeBundle(BundleEntry be, Writer out) {
         try {
-            out.write("<li>");
-
+            out.write("<tr class=\"content\"><td class=\"content\">");
             if (!be.isInstalled()) {
-                out.write("I ");
+                out.write("Install");
             } else if (be.isUpdated()) {
-                out.write("U ");
+                out.write("Update");
             } else {
-                out.write("N ");
+                out.write("No Action");
             }
 
-            out.write(be.getSymbolicName() + " v" + be.getVersion());
-            out.write("</li>");
+            out.write("<td class=\"content\">" + be.getSymbolicName() + "</td>");
+            out.write("<td class=\"content\">" + be.getVersion() + "</td>");
+            out.write("<td class=\"content\">" + be.getStart() + "</td>");
+            out.write("</tr>");
         } catch (IOException e) {
             log.error("This really shouldn't happen", e);
         }
     }
+
+    private void writeConfig(ConfigEntry cfg, Writer out) {
+        try {
+            out.write("<tr class=\"content\"><td class=\"content\" colspan=\"4\">" + cfg.getPid() + "</td></tr>");
+            out.write("</tr>");
+        } catch (IOException e) {
+            log.error("This really shouldn't happen", e);
+        }
+    }
+
+    private void writeUpgradeInfo(HttpServletResponse resp, InputStream jarIs) throws IOException {
+        Writer out = resp.getWriter();
+        UpgradeRequest request = upgradeService.readSlingJar(jarIs);
+
+        out.write("<table class=\"content\" cellspacing=\"0\" cellpadding=\"0\">");
+        out.write("<thead><tr><th class=\"content container\" colspan=\"4\">" + request.getTitle() + " version "
+                + request.getVersion());
+        out.write("<br/><small>" + request.getVendor() + "</small></th></tr></thead>");
+
+        out.write("<tbody><tr><th class=\"content container\" colspan=\"4\">Startup Bundles</h3><th></tr>");
+        out.write(
+                "<tr><th class=\"content\">Action</th><th class=\"content\">Bundle</th><th class=\"content\">Version</th><th class=\"content\">Start Level</th></tr>");
+        request.getStartupBundles().forEach(e -> {
+            writeBundle(e, out);
+        });
+        out.write("</tbody>");
+
+        out.write("<tbody><tr><th class=\"content container\" colspan=\"4\">Install Bundles</h3><th></tr>");
+        out.write(
+                "<tr><th class=\"content\">Action</th><th class=\"content\">Bundle</th><th class=\"content\">Version</th><th class=\"content\">Start Level</th></tr>");
+        request.getBundles().forEach(e -> {
+            writeBundle(e, out);
+        });
+
+        out.write("</tbody>");
+
+        out.write("<tbody><tr><th class=\"content container\" colspan=\"4\">Configurations</h3><th></tr>");
+        out.write("<tr><th class=\"content\" colspan=\"4\">Configuration PID</th></tr>");
+        request.getConfigs().forEach(e -> {
+            writeConfig(e, out);
+        });
+        out.write("</tbody></table>");
+    }
 }


[sling-whiteboard] 02/02: Added code to read configurations and repoinits

Posted by dk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dklco pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git

commit 9c90471714104db9a6338ecb017e558f994fc8b1
Author: Dan Klco <dk...@apache.org>
AuthorDate: Wed Nov 7 00:44:31 2018 -0500

    Added code to read configurations and repoinits
---
 .../sling-org-apache-sling-scripting-sightly       |   1 -
 upgrade/pom.xml                                    |  20 +++
 .../java/org/apache/sling/upgrade/BundleEntry.java |  51 +++----
 .../java/org/apache/sling/upgrade/ConfigEntry.java |  39 +++--
 ...upBundleEntry.java => EntryHandlerFactory.java} |  34 +++--
 .../org/apache/sling/upgrade/RepoInitEntry.java    |  74 +++++++++
 .../apache/sling/upgrade/StartupBundleEntry.java   |   6 -
 .../{StartupBundleEntry.java => UpgradeEntry.java} |  25 ++--
 .../org/apache/sling/upgrade/UpgradeRequest.java   |  26 +---
 .../sling/upgrade/impl/BundleEntryFactory.java     |  64 ++++++++
 .../ConfigEntryFactory.java}                       |  24 +--
 .../RepoinitEntryFactory.java}                     |  33 +++--
 .../StartupBundleEntryFactory.java}                |  30 ++--
 .../sling/upgrade/impl/UpgradeServiceImpl.java     |  32 ++--
 .../sling/upgrade/impl/UpgradeWebConsole.java      | 165 ++++++++++++++-------
 upgrade/src/main/resources/bundleentry.html        |  11 ++
 upgrade/src/main/resources/bundles.html            |  19 +++
 upgrade/src/main/resources/config.html             |  18 +++
 upgrade/src/main/resources/configentry.html        |  10 ++
 upgrade/src/main/resources/form.html               |  35 +++++
 upgrade/src/main/resources/header.html             |  14 ++
 upgrade/src/main/resources/repoinit.html           |  16 ++
 upgrade/src/main/resources/repoinitentry.html      |  15 ++
 .../apache/sling/upgrade/UpgradeServiceTest.java   |  39 ++++-
 24 files changed, 594 insertions(+), 207 deletions(-)

diff --git a/scripting-resolver/sling-org-apache-sling-scripting-sightly b/scripting-resolver/sling-org-apache-sling-scripting-sightly
deleted file mode 160000
index 46638f1..0000000
--- a/scripting-resolver/sling-org-apache-sling-scripting-sightly
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 46638f1f34f6d3435102fd5e282943cb4127a5be
diff --git a/upgrade/pom.xml b/upgrade/pom.xml
index 69483e9..5e74b99 100644
--- a/upgrade/pom.xml
+++ b/upgrade/pom.xml
@@ -113,6 +113,26 @@
             <scope>provided</scope>
         </dependency>
 
+        <!-- Sling Dependencies -->
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.provisioning.model</artifactId>
+            <version>1.8.4</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.repoinit.parser</artifactId>
+            <version>1.2.2</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.settings</artifactId>
+            <version>1.3.10</version>
+            <scope>provided</scope>
+        </dependency>
+
         <!-- Test Dependencies -->
         <dependency>
             <groupId>junit</groupId>
diff --git a/upgrade/src/main/java/org/apache/sling/upgrade/BundleEntry.java b/upgrade/src/main/java/org/apache/sling/upgrade/BundleEntry.java
index f607e2f..c1efc85 100644
--- a/upgrade/src/main/java/org/apache/sling/upgrade/BundleEntry.java
+++ b/upgrade/src/main/java/org/apache/sling/upgrade/BundleEntry.java
@@ -23,9 +23,7 @@ import java.util.jar.Attributes;
 import java.util.jar.JarEntry;
 import java.util.jar.JarInputStream;
 import java.util.jar.Manifest;
-import java.util.regex.Pattern;
 
-import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang.StringUtils;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
@@ -37,37 +35,30 @@ import org.slf4j.LoggerFactory;
  * Represents a bundle entry loaded from a Sling JAR. Contains the bundle
  * manifest data, start level, bundle contents and installation requirements.
  */
-public class BundleEntry implements Comparable<BundleEntry> {
+public class BundleEntry extends UpgradeEntry {
     private static final String BUNDLE_SYMBOLIC_NAME = "Bundle-SymbolicName";
 
-    private static final Pattern ENTRY_PATTERN = Pattern.compile("resources\\/install\\/\\d{1,2}\\/[\\w\\-\\.]+\\.jar");
-
     private static final Logger log = LoggerFactory.getLogger(BundleEntry.class);
 
-    public static boolean matches(JarEntry entry) {
-        return ENTRY_PATTERN.matcher(entry.getName()).matches();
-    }
-
-    private final byte[] contents;
-
     private final boolean installed;
-
+    private final String runmode;
     private final int start;
-
     private final String symbolicName;
-
     private final boolean updated;
     private final Version version;
 
     public BundleEntry(JarEntry entry, InputStream is, BundleContext bundleContext) throws IOException {
-        String startStr = entry.getName().split("\\/")[2];
+        super(entry, is);
+        String[] segments = entry.getName().split("\\/");
+        String startStr = segments[2];
+        if (segments[1].contains(".")) {
+            runmode = StringUtils.substringAfter(segments[1], ".");
+        } else {
+            runmode = null;
+        }
         start = Integer.parseInt(startStr, 10);
         log.debug("Loaded start level {}", start);
-
-        log.debug("Reading bundle contents");
-        contents = IOUtils.toByteArray(is);
-        log.debug("Loaded {} bytes", contents.length);
-        try (JarInputStream bundleIs = new JarInputStream(new ByteArrayInputStream(contents))) {
+        try (JarInputStream bundleIs = new JarInputStream(new ByteArrayInputStream(this.getContents()))) {
 
             Manifest manifest = bundleIs.getManifest();
             Attributes attributes = manifest.getMainAttributes();
@@ -103,11 +94,16 @@ public class BundleEntry implements Comparable<BundleEntry> {
     }
 
     @Override
-    public int compareTo(BundleEntry o) {
-        if (this.start != o.start) {
-            return this.start - o.start;
+    public int compareTo(UpgradeEntry o) {
+        if (o instanceof BundleEntry) {
+            BundleEntry bo = (BundleEntry) o;
+            if (this.start != bo.start) {
+                return this.start - bo.start;
+            } else {
+                return symbolicName.compareTo(bo.symbolicName);
+            }
         } else {
-            return symbolicName.compareTo(o.symbolicName);
+            return getClass().getName().compareTo(o.getClass().getName());
         }
     }
 
@@ -138,11 +134,8 @@ public class BundleEntry implements Comparable<BundleEntry> {
         return true;
     }
 
-    /**
-     * @return the contents
-     */
-    public byte[] getContents() {
-        return contents;
+    public String getRunmode() {
+        return runmode;
     }
 
     public int getStart() {
diff --git a/upgrade/src/main/java/org/apache/sling/upgrade/ConfigEntry.java b/upgrade/src/main/java/org/apache/sling/upgrade/ConfigEntry.java
index 43d840a..6b18e41 100644
--- a/upgrade/src/main/java/org/apache/sling/upgrade/ConfigEntry.java
+++ b/upgrade/src/main/java/org/apache/sling/upgrade/ConfigEntry.java
@@ -16,12 +16,12 @@
  */
 package org.apache.sling.upgrade;
 
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.jar.JarEntry;
-import java.util.regex.Pattern;
 
-import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -29,27 +29,27 @@ import org.slf4j.LoggerFactory;
  * Represents a configuration entry loaded from a Sling JAR. Contains the
  * configuration contents and installation requirements.
  */
-public class ConfigEntry {
-    private static final Pattern ENTRY_PATTERN = Pattern.compile("resources\\/config\\/[\\w\\-\\.]+\\.config");
+public class ConfigEntry extends UpgradeEntry {
 
     private static final Logger log = LoggerFactory.getLogger(ConfigEntry.class);
 
-    public static final boolean matches(JarEntry entry) {
-        return ENTRY_PATTERN.matcher(entry.getName()).matches();
-    }
-
-    private final byte[] contents;
-
     private final String pid;
 
     public ConfigEntry(JarEntry entry, InputStream is) throws IOException {
-
+        super(entry, is);
         pid = entry.getName().replace("resources/config/", "");
         log.debug("Reading config {}", pid);
 
-        contents = IOUtils.toByteArray(is);
-        log.debug("Read config contents");
+    }
 
+    @Override
+    public int compareTo(UpgradeEntry o) {
+        if (o instanceof ConfigEntry) {
+            ConfigEntry co = (ConfigEntry) o;
+            return pid.compareTo(co.getPid());
+        } else {
+            return getClass().getName().compareTo(o.getClass().getName());
+        }
     }
 
     /*
@@ -80,19 +80,18 @@ public class ConfigEntry {
     }
 
     /**
-     * @return the contents
-     */
-    public byte[] getContents() {
-        return contents;
-    }
-
-    /**
      * @return the pid
      */
     public String getPid() {
         return pid;
     }
 
+    public String getPath() {
+        String path = StringUtils.substringBeforeLast(pid, ".");
+        path = path.replace('.', File.separatorChar).replace("-", "%007e");
+        return path + ".config";
+    }
+
     /*
      * (non-Javadoc)
      * 
diff --git a/upgrade/src/main/java/org/apache/sling/upgrade/StartupBundleEntry.java b/upgrade/src/main/java/org/apache/sling/upgrade/EntryHandlerFactory.java
similarity index 53%
copy from upgrade/src/main/java/org/apache/sling/upgrade/StartupBundleEntry.java
copy to upgrade/src/main/java/org/apache/sling/upgrade/EntryHandlerFactory.java
index c89bc52..6ac5114 100644
--- a/upgrade/src/main/java/org/apache/sling/upgrade/StartupBundleEntry.java
+++ b/upgrade/src/main/java/org/apache/sling/upgrade/EntryHandlerFactory.java
@@ -19,23 +19,29 @@ package org.apache.sling.upgrade;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.jar.JarEntry;
-import java.util.regex.Pattern;
-
-import org.osgi.framework.BundleContext;
 
 /**
- * Represents a bundle entry loaded from a Sling JAR. Contains the bundle
- * manifest data, start level, bundle contents and installation requirements.
+ * A service factory for Entry handlers to implement. These services are called
+ * to process entries to upgrade an Apache Sling instance.
  */
-public class StartupBundleEntry extends BundleEntry {
-    private static final Pattern ENTRY_PATTERN = Pattern.compile("resources\\/bundles\\/\\d{1,2}\\/[\\w\\-\\.]+\\.jar");
-
-    public static boolean matches(JarEntry entry) {
-        return ENTRY_PATTERN.matcher(entry.getName()).matches();
-    }
+public interface EntryHandlerFactory<E extends UpgradeEntry> {
 
-    public StartupBundleEntry(JarEntry entry, InputStream is, BundleContext bundleContext) throws IOException {
-        super(entry, is, bundleContext);
-    }
+    /**
+     * Returns true if the entry matches the requirements for this entry provider
+     * and should be loaded.
+     * 
+     * @param entry the JarEntry to check
+     * @return true if matches, false otherwise
+     */
+    boolean matches(JarEntry entry);
 
+    /**
+     * Load an upgrade entry from the Specified Jar entry.
+     * 
+     * @param entry the entry to to load the upgrade entry from
+     * @param is    the input stream to load the entry contents from
+     * @return the upgrade entry
+     * @throws IOException 
+     */
+    E loadEntry(JarEntry entry, InputStream is) throws IOException;
 }
diff --git a/upgrade/src/main/java/org/apache/sling/upgrade/RepoInitEntry.java b/upgrade/src/main/java/org/apache/sling/upgrade/RepoInitEntry.java
new file mode 100644
index 0000000..e10d52d
--- /dev/null
+++ b/upgrade/src/main/java/org/apache/sling/upgrade/RepoInitEntry.java
@@ -0,0 +1,74 @@
+/*
+ * 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.sling.upgrade;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringReader;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.jar.JarEntry;
+
+import org.apache.sling.provisioning.model.Model;
+import org.apache.sling.provisioning.model.io.ModelReader;
+import org.apache.sling.repoinit.parser.RepoInitParser;
+import org.apache.sling.repoinit.parser.RepoInitParsingException;
+import org.apache.sling.repoinit.parser.operations.Operation;
+
+/**
+ * Represents a repoinit file.
+ */
+public class RepoInitEntry extends UpgradeEntry {
+
+    private final Model model;
+    private final Map<String,List<Operation>> repoinits = new HashMap<>();
+
+    public RepoInitEntry(JarEntry entry, InputStream is, RepoInitParser parser)
+            throws IOException, RepoInitParsingException {
+        super(entry, is);
+        model = ModelReader.read(new InputStreamReader(new ByteArrayInputStream(super.getContents())), null);
+        model.getFeatures().forEach(f -> f.getAdditionalSections("repoinit").forEach(r -> {
+            try {
+                repoinits.put(f.getName(), parser.parse(new StringReader(r.getContents())));
+            } catch (RepoInitParsingException e) {
+                throw new RuntimeException("Failed to parse repoinit file",e);
+            }
+        }));
+
+    }
+    
+    @Override
+    public int compareTo(UpgradeEntry o) {
+        if (o instanceof RepoInitEntry) {
+            return this.getOriginalName().compareTo(o.getOriginalName());
+        } else {
+            return getClass().getName().compareTo(o.getClass().getName());
+        }
+    }
+
+    public Model getModel() {
+        return model;
+    }
+
+    public Map<String,List<Operation>> getRepoInits(){
+        return repoinits;
+    }
+
+}
diff --git a/upgrade/src/main/java/org/apache/sling/upgrade/StartupBundleEntry.java b/upgrade/src/main/java/org/apache/sling/upgrade/StartupBundleEntry.java
index c89bc52..e5978cc 100644
--- a/upgrade/src/main/java/org/apache/sling/upgrade/StartupBundleEntry.java
+++ b/upgrade/src/main/java/org/apache/sling/upgrade/StartupBundleEntry.java
@@ -19,7 +19,6 @@ package org.apache.sling.upgrade;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.jar.JarEntry;
-import java.util.regex.Pattern;
 
 import org.osgi.framework.BundleContext;
 
@@ -28,11 +27,6 @@ import org.osgi.framework.BundleContext;
  * manifest data, start level, bundle contents and installation requirements.
  */
 public class StartupBundleEntry extends BundleEntry {
-    private static final Pattern ENTRY_PATTERN = Pattern.compile("resources\\/bundles\\/\\d{1,2}\\/[\\w\\-\\.]+\\.jar");
-
-    public static boolean matches(JarEntry entry) {
-        return ENTRY_PATTERN.matcher(entry.getName()).matches();
-    }
 
     public StartupBundleEntry(JarEntry entry, InputStream is, BundleContext bundleContext) throws IOException {
         super(entry, is, bundleContext);
diff --git a/upgrade/src/main/java/org/apache/sling/upgrade/StartupBundleEntry.java b/upgrade/src/main/java/org/apache/sling/upgrade/UpgradeEntry.java
similarity index 58%
copy from upgrade/src/main/java/org/apache/sling/upgrade/StartupBundleEntry.java
copy to upgrade/src/main/java/org/apache/sling/upgrade/UpgradeEntry.java
index c89bc52..de97e38 100644
--- a/upgrade/src/main/java/org/apache/sling/upgrade/StartupBundleEntry.java
+++ b/upgrade/src/main/java/org/apache/sling/upgrade/UpgradeEntry.java
@@ -19,23 +19,28 @@ package org.apache.sling.upgrade;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.jar.JarEntry;
-import java.util.regex.Pattern;
 
-import org.osgi.framework.BundleContext;
+import org.apache.commons.io.IOUtils;
 
 /**
- * Represents a bundle entry loaded from a Sling JAR. Contains the bundle
- * manifest data, start level, bundle contents and installation requirements.
+ * Represents an entry loaded from the upgrade JAR.
  */
-public class StartupBundleEntry extends BundleEntry {
-    private static final Pattern ENTRY_PATTERN = Pattern.compile("resources\\/bundles\\/\\d{1,2}\\/[\\w\\-\\.]+\\.jar");
+public abstract class UpgradeEntry implements Comparable<UpgradeEntry> {
 
-    public static boolean matches(JarEntry entry) {
-        return ENTRY_PATTERN.matcher(entry.getName()).matches();
+    private final byte[] contents;
+
+    private final String originalName;
+
+    public UpgradeEntry(JarEntry entry, InputStream is) throws IOException {
+        originalName = entry.getName();
+        contents = IOUtils.toByteArray(is);
     }
 
-    public StartupBundleEntry(JarEntry entry, InputStream is, BundleContext bundleContext) throws IOException {
-        super(entry, is, bundleContext);
+    public byte[] getContents() {
+        return contents;
     }
 
+    public String getOriginalName() {
+        return originalName;
+    }
 }
diff --git a/upgrade/src/main/java/org/apache/sling/upgrade/UpgradeRequest.java b/upgrade/src/main/java/org/apache/sling/upgrade/UpgradeRequest.java
index 759ad78..ec5058b 100644
--- a/upgrade/src/main/java/org/apache/sling/upgrade/UpgradeRequest.java
+++ b/upgrade/src/main/java/org/apache/sling/upgrade/UpgradeRequest.java
@@ -20,15 +20,14 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.jar.Attributes;
 import java.util.jar.Manifest;
+import java.util.stream.Collectors;
 
 /**
  * Represents a request to update Apache Sling.
  */
 public class UpgradeRequest {
 
-    private final List<BundleEntry> bundles = new ArrayList<>();
-    private final List<ConfigEntry> configs = new ArrayList<>();
-    private final List<BundleEntry> startupBundles = new ArrayList<>();
+    private final List<UpgradeEntry> entries = new ArrayList<>();
     private final String title;
     private final String vendor;
     private final String version;
@@ -41,24 +40,15 @@ public class UpgradeRequest {
     }
 
     /**
-     * @return the bundles
+     * @return the entries
      */
-    public List<BundleEntry> getBundles() {
-        return bundles;
+    public List<UpgradeEntry> getEntries() {
+        return entries;
     }
 
-    /**
-     * @return the configs
-     */
-    public List<ConfigEntry> getConfigs() {
-        return configs;
-    }
-
-    /**
-     * @return the startupBundles
-     */
-    public List<BundleEntry> getStartupBundles() {
-        return startupBundles;
+    @SuppressWarnings("unchecked")
+    public <E extends UpgradeEntry> List<E> getEntriesByType(Class<E> type) {
+        return entries.stream().filter(e -> e.getClass().equals(type)).map(e -> (E) e).collect(Collectors.toList());
     }
 
     /**
diff --git a/upgrade/src/main/java/org/apache/sling/upgrade/impl/BundleEntryFactory.java b/upgrade/src/main/java/org/apache/sling/upgrade/impl/BundleEntryFactory.java
new file mode 100644
index 0000000..12a306e
--- /dev/null
+++ b/upgrade/src/main/java/org/apache/sling/upgrade/impl/BundleEntryFactory.java
@@ -0,0 +1,64 @@
+/*
+ * 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.sling.upgrade.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.jar.JarEntry;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.sling.settings.SlingSettingsService;
+import org.apache.sling.upgrade.BundleEntry;
+import org.apache.sling.upgrade.EntryHandlerFactory;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+@Component(service = { EntryHandlerFactory.class }, immediate = true)
+public class BundleEntryFactory implements EntryHandlerFactory<BundleEntry> {
+
+    @Reference
+    private SlingSettingsService settingsService;
+
+    private static final Pattern ENTRY_PATTERN = Pattern
+            .compile("resources\\/install(.[a-z_]+)?\\/\\d{1,2}\\/[\\w\\-\\.]+\\.jar");
+    private BundleContext bundleContext;
+
+    @Override
+    public boolean matches(JarEntry entry) {
+        boolean matches = ENTRY_PATTERN.matcher(entry.getName()).matches();
+        if (matches && entry.getName().split("\\/")[1].contains(".")) {
+            String runmode = StringUtils.substringAfter(entry.getName().split("\\/")[1], ".");
+            return settingsService.getRunModes().contains(runmode);
+        }
+        return matches;
+    }
+
+    @Activate
+    public void activate(ComponentContext context) {
+        this.bundleContext = context.getBundleContext();
+    }
+
+    @Override
+    public BundleEntry loadEntry(JarEntry entry, InputStream is) throws IOException {
+        return new BundleEntry(entry, is, bundleContext);
+    }
+
+}
diff --git a/upgrade/src/main/java/org/apache/sling/upgrade/StartupBundleEntry.java b/upgrade/src/main/java/org/apache/sling/upgrade/impl/ConfigEntryFactory.java
similarity index 64%
copy from upgrade/src/main/java/org/apache/sling/upgrade/StartupBundleEntry.java
copy to upgrade/src/main/java/org/apache/sling/upgrade/impl/ConfigEntryFactory.java
index c89bc52..6edb331 100644
--- a/upgrade/src/main/java/org/apache/sling/upgrade/StartupBundleEntry.java
+++ b/upgrade/src/main/java/org/apache/sling/upgrade/impl/ConfigEntryFactory.java
@@ -14,28 +14,30 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sling.upgrade;
+package org.apache.sling.upgrade.impl;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.jar.JarEntry;
 import java.util.regex.Pattern;
 
-import org.osgi.framework.BundleContext;
+import org.apache.sling.upgrade.ConfigEntry;
+import org.apache.sling.upgrade.EntryHandlerFactory;
+import org.osgi.service.component.annotations.Component;
 
-/**
- * Represents a bundle entry loaded from a Sling JAR. Contains the bundle
- * manifest data, start level, bundle contents and installation requirements.
- */
-public class StartupBundleEntry extends BundleEntry {
-    private static final Pattern ENTRY_PATTERN = Pattern.compile("resources\\/bundles\\/\\d{1,2}\\/[\\w\\-\\.]+\\.jar");
+@Component(service = { EntryHandlerFactory.class }, immediate = true)
+public class ConfigEntryFactory implements EntryHandlerFactory<ConfigEntry> {
+
+    private static final Pattern ENTRY_PATTERN = Pattern.compile("resources\\/config\\/[\\w\\-\\.]+\\.config");
 
-    public static boolean matches(JarEntry entry) {
+    @Override
+    public boolean matches(JarEntry entry) {
         return ENTRY_PATTERN.matcher(entry.getName()).matches();
     }
 
-    public StartupBundleEntry(JarEntry entry, InputStream is, BundleContext bundleContext) throws IOException {
-        super(entry, is, bundleContext);
+    @Override
+    public ConfigEntry loadEntry(JarEntry entry, InputStream is) throws IOException {
+        return new ConfigEntry(entry, is);
     }
 
 }
diff --git a/upgrade/src/main/java/org/apache/sling/upgrade/StartupBundleEntry.java b/upgrade/src/main/java/org/apache/sling/upgrade/impl/RepoinitEntryFactory.java
similarity index 52%
copy from upgrade/src/main/java/org/apache/sling/upgrade/StartupBundleEntry.java
copy to upgrade/src/main/java/org/apache/sling/upgrade/impl/RepoinitEntryFactory.java
index c89bc52..5b11539 100644
--- a/upgrade/src/main/java/org/apache/sling/upgrade/StartupBundleEntry.java
+++ b/upgrade/src/main/java/org/apache/sling/upgrade/impl/RepoinitEntryFactory.java
@@ -14,28 +14,39 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sling.upgrade;
+package org.apache.sling.upgrade.impl;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.jar.JarEntry;
 import java.util.regex.Pattern;
 
-import org.osgi.framework.BundleContext;
+import org.apache.sling.repoinit.parser.RepoInitParser;
+import org.apache.sling.repoinit.parser.RepoInitParsingException;
+import org.apache.sling.upgrade.EntryHandlerFactory;
+import org.apache.sling.upgrade.RepoInitEntry;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
 
-/**
- * Represents a bundle entry loaded from a Sling JAR. Contains the bundle
- * manifest data, start level, bundle contents and installation requirements.
- */
-public class StartupBundleEntry extends BundleEntry {
-    private static final Pattern ENTRY_PATTERN = Pattern.compile("resources\\/bundles\\/\\d{1,2}\\/[\\w\\-\\.]+\\.jar");
+@Component(service = { EntryHandlerFactory.class }, immediate = true)
+public class RepoinitEntryFactory implements EntryHandlerFactory<RepoInitEntry> {
+
+    private static final Pattern ENTRY_PATTERN = Pattern.compile("resources\\/provisioning\\/[\\w]+\\.txt");
+
+    @Reference
+    private RepoInitParser parser;
 
-    public static boolean matches(JarEntry entry) {
+    @Override
+    public boolean matches(JarEntry entry) {
         return ENTRY_PATTERN.matcher(entry.getName()).matches();
     }
 
-    public StartupBundleEntry(JarEntry entry, InputStream is, BundleContext bundleContext) throws IOException {
-        super(entry, is, bundleContext);
+    public RepoInitEntry loadEntry(JarEntry entry, InputStream is) throws IOException {
+        try {
+            return new RepoInitEntry(entry, is, parser);
+        } catch (RepoInitParsingException e) {
+            throw new IOException("Exception parsing repoinit file", e);
+        }
     }
 
 }
diff --git a/upgrade/src/main/java/org/apache/sling/upgrade/StartupBundleEntry.java b/upgrade/src/main/java/org/apache/sling/upgrade/impl/StartupBundleEntryFactory.java
similarity index 57%
copy from upgrade/src/main/java/org/apache/sling/upgrade/StartupBundleEntry.java
copy to upgrade/src/main/java/org/apache/sling/upgrade/impl/StartupBundleEntryFactory.java
index c89bc52..25a4989 100644
--- a/upgrade/src/main/java/org/apache/sling/upgrade/StartupBundleEntry.java
+++ b/upgrade/src/main/java/org/apache/sling/upgrade/impl/StartupBundleEntryFactory.java
@@ -14,28 +14,40 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sling.upgrade;
+package org.apache.sling.upgrade.impl;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.jar.JarEntry;
 import java.util.regex.Pattern;
 
+import org.apache.sling.upgrade.EntryHandlerFactory;
+import org.apache.sling.upgrade.StartupBundleEntry;
 import org.osgi.framework.BundleContext;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+
+@Component(service = { EntryHandlerFactory.class }, immediate = true)
+public class StartupBundleEntryFactory implements EntryHandlerFactory<StartupBundleEntry> {
 
-/**
- * Represents a bundle entry loaded from a Sling JAR. Contains the bundle
- * manifest data, start level, bundle contents and installation requirements.
- */
-public class StartupBundleEntry extends BundleEntry {
     private static final Pattern ENTRY_PATTERN = Pattern.compile("resources\\/bundles\\/\\d{1,2}\\/[\\w\\-\\.]+\\.jar");
 
-    public static boolean matches(JarEntry entry) {
+    private BundleContext bundleContext;
+
+    @Override
+    public boolean matches(JarEntry entry) {
         return ENTRY_PATTERN.matcher(entry.getName()).matches();
     }
 
-    public StartupBundleEntry(JarEntry entry, InputStream is, BundleContext bundleContext) throws IOException {
-        super(entry, is, bundleContext);
+    @Activate
+    public void activate(ComponentContext context) {
+        this.bundleContext = context.getBundleContext();
+    }
+
+    @Override
+    public StartupBundleEntry loadEntry(JarEntry entry, InputStream is) throws IOException {
+        return new StartupBundleEntry(entry, is, bundleContext);
     }
 
 }
diff --git a/upgrade/src/main/java/org/apache/sling/upgrade/impl/UpgradeServiceImpl.java b/upgrade/src/main/java/org/apache/sling/upgrade/impl/UpgradeServiceImpl.java
index 92aff4d..88c340e 100644
--- a/upgrade/src/main/java/org/apache/sling/upgrade/impl/UpgradeServiceImpl.java
+++ b/upgrade/src/main/java/org/apache/sling/upgrade/impl/UpgradeServiceImpl.java
@@ -19,19 +19,16 @@ package org.apache.sling.upgrade.impl;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Collections;
+import java.util.List;
 import java.util.jar.JarEntry;
 import java.util.jar.JarInputStream;
 import java.util.jar.Manifest;
 
-import org.apache.sling.upgrade.BundleEntry;
-import org.apache.sling.upgrade.ConfigEntry;
-import org.apache.sling.upgrade.StartupBundleEntry;
+import org.apache.sling.upgrade.EntryHandlerFactory;
 import org.apache.sling.upgrade.UpgradeRequest;
 import org.apache.sling.upgrade.UpgradeService;
-import org.osgi.framework.BundleContext;
-import org.osgi.service.component.ComponentContext;
-import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -40,12 +37,8 @@ public class UpgradeServiceImpl implements UpgradeService {
 
     private static final Logger log = LoggerFactory.getLogger(UpgradeServiceImpl.class);
 
-    private BundleContext bundleContext;
-
-    @Activate
-    public void activate(ComponentContext context) {
-        bundleContext = context.getBundleContext();
-    }
+    @Reference
+    private List<EntryHandlerFactory<?>> entryFactories;
 
     @Override
     public UpgradeRequest readSlingJar(InputStream jar) throws IOException {
@@ -60,17 +53,18 @@ public class UpgradeServiceImpl implements UpgradeService {
             JarEntry entry = jarIs.getNextJarEntry();
             while (entry != null) {
                 log.debug("Reading entry {}", entry.getName());
-                if (BundleEntry.matches(entry)) {
-                    request.getBundles().add(new BundleEntry(entry, jarIs, bundleContext));
-                } else if (ConfigEntry.matches(entry)) {
-                    request.getConfigs().add(new ConfigEntry(entry, jarIs));
-                } else if (StartupBundleEntry.matches(entry)) {
-                    request.getStartupBundles().add(new StartupBundleEntry(entry, jarIs, bundleContext));
+
+                for (EntryHandlerFactory<?> factory : entryFactories) {
+                    if (factory.matches(entry)) {
+                        log.debug("Loading with {}", factory.getClass().getName());
+                        request.getEntries().add(factory.loadEntry(entry, jarIs));
+                        break;
+                    }
                 }
                 entry = jarIs.getNextJarEntry();
             }
 
-            Collections.sort(request.getBundles());
+            Collections.sort(request.getEntries());
         }
         return request;
     }
diff --git a/upgrade/src/main/java/org/apache/sling/upgrade/impl/UpgradeWebConsole.java b/upgrade/src/main/java/org/apache/sling/upgrade/impl/UpgradeWebConsole.java
index 71b74c8..3f26402 100644
--- a/upgrade/src/main/java/org/apache/sling/upgrade/impl/UpgradeWebConsole.java
+++ b/upgrade/src/main/java/org/apache/sling/upgrade/impl/UpgradeWebConsole.java
@@ -23,7 +23,11 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.PrintWriter;
 import java.io.Writer;
+import java.nio.charset.Charset;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 import javax.servlet.Servlet;
 import javax.servlet.ServletConfig;
@@ -35,9 +39,13 @@ import org.apache.commons.fileupload.FileItem;
 import org.apache.commons.fileupload.FileUploadException;
 import org.apache.commons.fileupload.disk.DiskFileItemFactory;
 import org.apache.commons.fileupload.servlet.ServletFileUpload;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.text.StrSubstitutor;
 import org.apache.felix.webconsole.AbstractWebConsolePlugin;
 import org.apache.sling.upgrade.BundleEntry;
 import org.apache.sling.upgrade.ConfigEntry;
+import org.apache.sling.upgrade.RepoInitEntry;
+import org.apache.sling.upgrade.StartupBundleEntry;
 import org.apache.sling.upgrade.UpgradeRequest;
 import org.apache.sling.upgrade.UpgradeService;
 import org.osgi.service.component.annotations.Component;
@@ -54,6 +62,10 @@ import org.slf4j.LoggerFactory;
         "felix.webconsole.title=Upgrade", "felix.webconsole.category=Sling" })
 public class UpgradeWebConsole extends AbstractWebConsolePlugin {
 
+    private static final String CONTENT = "content";
+
+    private static final String TITLE = "title";
+
     static final String APP_ROOT = "upgrade";
 
     private static final Logger log = LoggerFactory.getLogger(UpgradeWebConsole.class);
@@ -163,46 +175,57 @@ public class UpgradeWebConsole extends AbstractWebConsolePlugin {
     protected void renderContent(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
         Writer out = res.getWriter();
 
-        out.write("<table class=\"content\" cellspacing=\"0\" width=\"100%\" cellpadding=\"0\">");
-        out.write("<thead><tr><th class=\"content container\">Update Apache Sling</th></tr></thead>");
-        out.write("<tbody><tr><td  class=\"content\">");
-        out.write("<form method=\"post\" enctype=\"multipart/form-data\">");
-        out.write(
-                "<div><label for=\"jar\">Sling Jar</lable><br/><input type=\"file\" name=\"jar\" accepts=\"application/java-archive\"></div>");
-        out.write(
-                "<div><label for=\"action\">Action</lable><br/><select name=\"action\"><option>Upgrade</option><option>Preview</option></div>");
-        out.write("<div><input type=\"submit\" value=\"Upload\" /></div>");
-        out.write("</form></td></tr></tbody></table>");
+        String template = getTemplate("form.html");
 
+        out.write(template);
     }
 
-    private void writeBundle(BundleEntry be, Writer out) {
+    private static String getTemplate(String name) {
         try {
-            out.write("<tr class=\"content\"><td class=\"content\">");
-            if (!be.isInstalled()) {
-                out.write("Install");
-            } else if (be.isUpdated()) {
-                out.write("Update");
-            } else {
-                out.write("No Action");
-            }
-
-            out.write("<td class=\"content\">" + be.getSymbolicName() + "</td>");
-            out.write("<td class=\"content\">" + be.getVersion() + "</td>");
-            out.write("<td class=\"content\">" + be.getStart() + "</td>");
-            out.write("</tr>");
+            return IOUtils.toString(UpgradeWebConsole.class.getClassLoader().getResourceAsStream(name));
         } catch (IOException e) {
-            log.error("This really shouldn't happen", e);
+            log.error("Exception loading template", e);
+            return "";
         }
     }
 
-    private void writeConfig(ConfigEntry cfg, Writer out) {
-        try {
-            out.write("<tr class=\"content\"><td class=\"content\" colspan=\"4\">" + cfg.getPid() + "</td></tr>");
-            out.write("</tr>");
-        } catch (IOException e) {
-            log.error("This really shouldn't happen", e);
+    private static String template(String name, Map<String, String> params) {
+        String template = getTemplate(name);
+        StrSubstitutor sub = new StrSubstitutor(params);
+        return sub.replace(template);
+    }
+
+    private static String renderBundle(BundleEntry be) {
+
+        String action;
+        if (!be.isInstalled()) {
+            action = "Install";
+        } else if (be.isUpdated()) {
+            action = "Update";
+        } else {
+            action = "No Action";
         }
+
+        return template("bundleentry.html", new HashMap<String, String>() {
+            private static final long serialVersionUID = 1L;
+            {
+                put("action", action);
+                put("symbolicName", be.getSymbolicName());
+                put("version", be.getVersion().toString());
+                put("start", String.valueOf(be.getStart()));
+            }
+        });
+    }
+
+    private static String renderConfig(ConfigEntry cfg) {
+        return template("configentry.html", new HashMap<String, String>() {
+            private static final long serialVersionUID = 1L;
+            {
+                put("pid", cfg.getPid());
+                put("path", cfg.getPath());
+                put(CONTENT, new String(cfg.getContents(), Charset.defaultCharset()).replace("\n", "<br/>"));
+            }
+        });
     }
 
     private void writeUpgradeInfo(HttpServletResponse resp, InputStream jarIs) throws IOException {
@@ -210,32 +233,66 @@ public class UpgradeWebConsole extends AbstractWebConsolePlugin {
         UpgradeRequest request = upgradeService.readSlingJar(jarIs);
 
         out.write("<table class=\"content\" cellspacing=\"0\" cellpadding=\"0\">");
-        out.write("<thead><tr><th class=\"content container\" colspan=\"4\">" + request.getTitle() + " version "
-                + request.getVersion());
-        out.write("<br/><small>" + request.getVendor() + "</small></th></tr></thead>");
-
-        out.write("<tbody><tr><th class=\"content container\" colspan=\"4\">Startup Bundles</h3><th></tr>");
-        out.write(
-                "<tr><th class=\"content\">Action</th><th class=\"content\">Bundle</th><th class=\"content\">Version</th><th class=\"content\">Start Level</th></tr>");
-        request.getStartupBundles().forEach(e -> {
-            writeBundle(e, out);
-        });
-        out.write("</tbody>");
+        out.write(template("header.html", new HashMap<String, String>() {
+            private static final long serialVersionUID = 1L;
+            {
+                put(TITLE, request.getTitle());
+                put("vendor", request.getVendor());
+                put("version", request.getVersion());
+            }
+        }));
+
+        out.write(template("bundles.html", new HashMap<String, String>() {
+            private static final long serialVersionUID = 1L;
+            {
+                put(CONTENT, request.getEntriesByType(StartupBundleEntry.class).stream()
+                        .map(UpgradeWebConsole::renderBundle).collect(Collectors.joining()));
+                put(TITLE, "Startup Bundles");
+            }
+        }));
+
+        out.write(template("bundles.html", new HashMap<String, String>() {
+            private static final long serialVersionUID = 1L;
+            {
+                put(CONTENT, request.getEntriesByType(BundleEntry.class).stream().map(UpgradeWebConsole::renderBundle)
+                        .collect(Collectors.joining()));
+                put(TITLE, "Install Bundles");
+            }
+        }));
 
-        out.write("<tbody><tr><th class=\"content container\" colspan=\"4\">Install Bundles</h3><th></tr>");
-        out.write(
-                "<tr><th class=\"content\">Action</th><th class=\"content\">Bundle</th><th class=\"content\">Version</th><th class=\"content\">Start Level</th></tr>");
-        request.getBundles().forEach(e -> {
-            writeBundle(e, out);
-        });
+        out.write(template("config.html", new HashMap<String, String>() {
+            private static final long serialVersionUID = 1L;
+            {
+                put(CONTENT, request.getEntriesByType(ConfigEntry.class).stream().map(UpgradeWebConsole::renderConfig)
+                        .collect(Collectors.joining()));
+            }
+        }));
+
+        out.write(template("repoinit.html", new HashMap<String, String>() {
+            private static final long serialVersionUID = 1L;
+            {
+                put(CONTENT, request.getEntriesByType(RepoInitEntry.class).stream()
+                        .map(UpgradeWebConsole::renderRepoInit).collect(Collectors.joining()));
+            }
+        }));
 
-        out.write("</tbody>");
+        out.write("</table>");
+    }
 
-        out.write("<tbody><tr><th class=\"content container\" colspan=\"4\">Configurations</h3><th></tr>");
-        out.write("<tr><th class=\"content\" colspan=\"4\">Configuration PID</th></tr>");
-        request.getConfigs().forEach(e -> {
-            writeConfig(e, out);
+    private static String renderRepoInit(RepoInitEntry ri) {
+
+        StringBuilder sb = new StringBuilder();
+        ri.getRepoInits().forEach((f, os) -> {
+            StringBuilder oStr = new StringBuilder();
+            os.forEach(o -> oStr.append("<li>" + o + "</li>"));
+            sb.append(template("repoinitentry.html", new HashMap<String, String>() {
+                private static final long serialVersionUID = 1L;
+                {
+                    put("feature", f);
+                    put(CONTENT, oStr.toString());
+                }
+            }));
         });
-        out.write("</tbody></table>");
+        return sb.toString();
     }
 }
diff --git a/upgrade/src/main/resources/bundleentry.html b/upgrade/src/main/resources/bundleentry.html
new file mode 100644
index 0000000..bb2430d
--- /dev/null
+++ b/upgrade/src/main/resources/bundleentry.html
@@ -0,0 +1,11 @@
+<!-- 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. -->
+<tr class="content">
+    <td class="content">${action}</td>
+    <td class="content">${symbolicName}</td>
+    <td class="content">${version}</td>
+    <td class="content">${start}</td>
+</tr>
\ No newline at end of file
diff --git a/upgrade/src/main/resources/bundles.html b/upgrade/src/main/resources/bundles.html
new file mode 100644
index 0000000..2a3d992
--- /dev/null
+++ b/upgrade/src/main/resources/bundles.html
@@ -0,0 +1,19 @@
+<!-- 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. -->
+<tbody>
+    <tr>
+        <th class="content container" colspan="4">
+            ${title}
+        <th>
+    </tr>
+    <tr>
+        <th class="content">Action</th>
+        <th class="content">Bundle</th>
+        <th class="content">Version</th>
+        <th class="content">Start Level</th>
+    </tr>
+    ${content}
+</tbody>
\ No newline at end of file
diff --git a/upgrade/src/main/resources/config.html b/upgrade/src/main/resources/config.html
new file mode 100644
index 0000000..6499a2f
--- /dev/null
+++ b/upgrade/src/main/resources/config.html
@@ -0,0 +1,18 @@
+<!-- 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. -->
+
+<tbody>
+    <tr>
+        <th class="content container" colspan="4">Configurations
+        <th>
+    </tr>
+    <tr>
+        <th class="content">PID</th>
+        <th class="content">Path</th>
+        <th class="content" colspan="2">Configuration</th>
+    </tr>
+    ${content}
+</tbody>
\ No newline at end of file
diff --git a/upgrade/src/main/resources/configentry.html b/upgrade/src/main/resources/configentry.html
new file mode 100644
index 0000000..ac6d40d
--- /dev/null
+++ b/upgrade/src/main/resources/configentry.html
@@ -0,0 +1,10 @@
+<!-- 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. -->
+<tr class="content">
+    <td class="content" colspan="1">${pid}</td>
+    <td class="content" colspan="1">${path}</td>
+    <td class="content" colspan="2"><pre>${content}</pre></td>
+</tr>
\ No newline at end of file
diff --git a/upgrade/src/main/resources/form.html b/upgrade/src/main/resources/form.html
new file mode 100644
index 0000000..111188a
--- /dev/null
+++ b/upgrade/src/main/resources/form.html
@@ -0,0 +1,35 @@
+<!-- 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. -->
+<table class="content" cellspacing="0" width="100%" cellpadding="0">
+    <thead>
+        <tr>
+            <th class="content container">Update Apache Sling</th>
+        </tr>
+    </thead>
+    <tbody>
+        <tr>
+            <td class="content">
+                <form method="post" enctype="multipart/form-data">
+                    <div>
+                        <label for="jar">Sling Jar</lable><br /> <input
+                            type="file" name="jar" accepts=".jar"
+                        />
+                    </div>
+                    <div>
+                        <label for="action">Action</lable><br />
+                        <select name="action">
+                                <option>Upgrade</option>
+                                <option>Preview</option>
+                        </select>
+                    </div>
+                    <div>
+                        <input type="submit" value="Upload" />
+                    </div>
+                </form>
+            </td>
+        </tr>
+    </tbody>
+</table>
\ No newline at end of file
diff --git a/upgrade/src/main/resources/header.html b/upgrade/src/main/resources/header.html
new file mode 100644
index 0000000..f43555c
--- /dev/null
+++ b/upgrade/src/main/resources/header.html
@@ -0,0 +1,14 @@
+<!-- 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. -->
+<thead>
+    <tr>
+        <th class="content container" colspan="4">
+            ${title} version ${version}
+            <br/>
+            <small>${vendor}</small>
+        </th>
+    </tr>
+</thead>
\ No newline at end of file
diff --git a/upgrade/src/main/resources/repoinit.html b/upgrade/src/main/resources/repoinit.html
new file mode 100644
index 0000000..1ed8a0a
--- /dev/null
+++ b/upgrade/src/main/resources/repoinit.html
@@ -0,0 +1,16 @@
+<!-- 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. -->
+<tbody>
+    <tr>
+        <th class="content container" colspan="4">RepoInit
+        <th>
+    </tr>
+    <tr>
+        <th class="content" colspan="1">Feature</th>
+        <th class="content" colspan="3">Operations</th>
+    </tr>
+    ${content}
+</tbody>
\ No newline at end of file
diff --git a/upgrade/src/main/resources/repoinitentry.html b/upgrade/src/main/resources/repoinitentry.html
new file mode 100644
index 0000000..0428b88
--- /dev/null
+++ b/upgrade/src/main/resources/repoinitentry.html
@@ -0,0 +1,15 @@
+<!-- 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. -->
+<tr class="content">
+    <td class="content" colspan="1">
+        ${feature}
+    </td>
+    <td class="content" colspan="3">
+        <ul>
+            ${content}
+        </ul>
+    </td>
+</tr>
diff --git a/upgrade/src/test/java/org/apache/sling/upgrade/UpgradeServiceTest.java b/upgrade/src/test/java/org/apache/sling/upgrade/UpgradeServiceTest.java
index 2ac03b6..54d3cee 100644
--- a/upgrade/src/test/java/org/apache/sling/upgrade/UpgradeServiceTest.java
+++ b/upgrade/src/test/java/org/apache/sling/upgrade/UpgradeServiceTest.java
@@ -23,8 +23,16 @@ import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
+import org.apache.sling.settings.SlingSettingsService;
+import org.apache.sling.upgrade.impl.BundleEntryFactory;
+import org.apache.sling.upgrade.impl.ConfigEntryFactory;
+import org.apache.sling.upgrade.impl.StartupBundleEntryFactory;
 import org.apache.sling.upgrade.impl.UpgradeServiceImpl;
 import org.junit.Before;
 import org.junit.Test;
@@ -47,7 +55,8 @@ public class UpgradeServiceTest {
     private UpgradeRequest request;
 
     @Before
-    public void init() throws IOException {
+    public void init() throws IOException, NoSuchFieldException, SecurityException, IllegalArgumentException,
+            IllegalAccessException {
         jar = getClass().getClassLoader().getResourceAsStream("sling.jar");
         upgradeService = new UpgradeServiceImpl();
 
@@ -72,8 +81,27 @@ public class UpgradeServiceTest {
         Bundle[] bundles = new Bundle[] { bundle1, bundle2, bundle3 };
         Mockito.when(bundleContext.getBundles()).thenReturn(bundles);
 
-        // Call the activator
-        ((UpgradeServiceImpl) upgradeService).activate(componentContext);
+        BundleEntryFactory bef = new BundleEntryFactory();
+        bef.activate(componentContext);
+        Field settingsService = bef.getClass().getDeclaredField("settingsService");
+        SlingSettingsService sso = Mockito.mock(SlingSettingsService.class);
+        Set<String> runmodes = new HashSet<>();
+        runmodes.add("oak_tar");
+        Mockito.when(sso.getRunModes()).thenReturn(runmodes);
+        settingsService.setAccessible(true);
+        settingsService.set(bef, sso);
+
+        StartupBundleEntryFactory sbef = new StartupBundleEntryFactory();
+        sbef.activate(componentContext);
+
+        List<EntryHandlerFactory<?>> factories = new ArrayList<>();
+        factories.add(bef);
+        factories.add(sbef);
+        factories.add(new ConfigEntryFactory());
+
+        Field field = upgradeService.getClass().getDeclaredField("entryFactories");
+        field.setAccessible(true);
+        field.set(upgradeService, factories);
 
         // read the request
         this.request = upgradeService.readSlingJar(jar);
@@ -94,7 +122,7 @@ public class UpgradeServiceTest {
     @Test
     public void testUpgradeRequestBundles() throws IOException {
         log.info("testUpgradeRequestBundles");
-        List<BundleEntry> bundles = request.getBundles();
+        List<BundleEntry> bundles = request.getEntriesByType(BundleEntry.class);
         assertNotNull(bundles);
 
         assertTrue(!bundles.isEmpty());
@@ -104,6 +132,7 @@ public class UpgradeServiceTest {
             assertNotNull(bundle.getStart());
             assertNotNull(bundle.getSymbolicName());
             assertNotNull(bundle.getVersion());
+            assertFalse("oak_mongo".equals(bundle.getRunmode()));
             switch (bundle.getSymbolicName()) {
             case "org.apache.sling.jcr.api":
                 assertTrue(bundle.isInstalled());
@@ -131,7 +160,7 @@ public class UpgradeServiceTest {
     @Test
     public void testUpgradeRequestConfigs() throws IOException {
         log.info("testUpgradeRequestConfigs");
-        List<ConfigEntry> configs = request.getConfigs();
+        List<ConfigEntry> configs = request.getEntriesByType(ConfigEntry.class);
         assertNotNull(configs);
 
         assertTrue(!configs.isEmpty());