You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2013/04/06 00:01:10 UTC

[8/8] git commit: Create T5Dashboard page

Create T5Dashboard page

Convert PageCatalog and ServiceStatus into page fragments, in the t5internal library.
Change the Hibernate Statistics page into a page fragment for the dashboard.


Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/04bf4d86
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/04bf4d86
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/04bf4d86

Branch: refs/heads/master
Commit: 04bf4d862eb4f98246bde66f818a497ed178f409
Parents: 1087fe8
Author: Howard M. Lewis Ship <hl...@apache.org>
Authored: Fri Apr 5 15:00:21 2013 -0700
Committer: Howard M. Lewis Ship <hl...@apache.org>
Committed: Fri Apr 5 15:00:21 2013 -0700

----------------------------------------------------------------------
 .../tapestry5/annotations/WhitelistAccessOnly.java |    5 +-
 .../tapestry5/corelib/pages/PageCatalog.java       |  325 ---------------
 .../tapestry5/corelib/pages/ServiceStatus.java     |   81 ----
 .../tapestry5/corelib/pages/T5Dashboard.java       |   81 ++++
 .../tapestry5/internal/PageCatalogTotals.java      |    4 +-
 .../internal/services/AssetSourceImpl.java         |    2 +-
 .../tapestry5/internal/services/PageSource.java    |    4 +-
 .../services/dashboard/DashboardManagerImpl.java   |   63 +++
 .../t5internal/components/InternalLayout.java      |    2 +-
 .../internal/t5internal/pages/PageCatalog.java     |  318 ++++++++++++++
 .../internal/t5internal/pages/ServiceStatus.java   |   82 ++++
 .../apache/tapestry5/services/TapestryModule.java  |    3 +-
 .../services/dashboard/DashboardManager.java       |   33 ++
 .../services/dashboard/DashboardModule.java        |   35 ++
 .../tapestry5/services/dashboard/DashboardTab.java |   44 ++
 .../tapestry5/services/dashboard/package-info.java |   18 +
 .../META-INF/assets/core/ServiceStatus.css         |   12 -
 .../resources/META-INF/assets/core/dashboard.css   |    7 +
 .../META-INF/assets/t5internal/service-status.css  |   12 +
 .../tapestry5/corelib/pages/PageCatalog.properties |    3 -
 .../apache/tapestry5/corelib/pages/PageCatalog.tml |   89 ----
 .../corelib/pages/ServiceStatus.properties         |   15 -
 .../tapestry5/corelib/pages/ServiceStatus.tml      |   64 ---
 .../apache/tapestry5/corelib/pages/T5Dashboard.tml |   30 ++
 .../t5internal/pages/PageCatalog.properties        |    3 +
 .../internal/t5internal/pages/PageCatalog.tml      |   78 ++++
 .../t5internal/pages/ServiceStatus.properties      |   15 +
 .../internal/t5internal/pages/ServiceStatus.tml    |   59 +++
 .../integration/app3/PageCatalogTests.groovy       |   10 +-
 .../integration/app1/CoreBehaviorsTests.java       |    6 +-
 .../tapestry5/hibernate/HibernateModule.java       |   11 +-
 .../tapestry5/hibernate/pages/Statistics.java      |   92 ----
 .../tapestry5/hibernate/pages/package-info.java    |   18 -
 .../t5internal/pages/HibernateStatistics.java      |   91 ++++
 .../hibernate/t5internal/pages/package-info.java   |   19 +
 .../hibernate/pages/Statistics.properties          |   15 -
 .../tapestry5/hibernate/pages/Statistics.tml       |   69 ---
 .../pages/HibernateStatistics.properties           |   15 +
 .../t5internal/pages/HibernateStatistics.tml       |   69 +++
 .../TapestryHibernateIntegrationTests.java         |    6 +-
 40 files changed, 1099 insertions(+), 809 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/main/java/org/apache/tapestry5/annotations/WhitelistAccessOnly.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/annotations/WhitelistAccessOnly.java b/tapestry-core/src/main/java/org/apache/tapestry5/annotations/WhitelistAccessOnly.java
index 082df24..b2b5e31 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/annotations/WhitelistAccessOnly.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/annotations/WhitelistAccessOnly.java
@@ -1,4 +1,4 @@
-// Copyright 2011 The Apache Software Foundation
+// Copyright 2011, 2013 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -26,8 +26,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 /**
  * Identifies a page that should only be accessed from clients on the whitelist. This is used for
- * some built-in pages ({@link org.apache.tapestry5.corelib.pages.PageCatalog} and {@link org.apache.tapestry5.corelib.pages.ServiceStatus}),
- * but can also be used in application pages.
+ * the  built-in {@link org.apache.tapestry5.corelib.pages.T5Dashboard} page, but can also be used in application pages.
  * <p/>
  * Tapestry does its best to make a request to a whitelist-only page that is accessed from a client not on the whitelist
  * appear to behave as if the page does not exist at all.

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/PageCatalog.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/PageCatalog.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/PageCatalog.java
deleted file mode 100644
index 2be477b..0000000
--- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/PageCatalog.java
+++ /dev/null
@@ -1,325 +0,0 @@
-// Copyright 2011, 2012 The Apache Software Foundation
-//
-// Licensed 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.tapestry5.corelib.pages;
-
-import org.apache.tapestry5.SymbolConstants;
-import org.apache.tapestry5.alerts.AlertManager;
-import org.apache.tapestry5.annotations.*;
-import org.apache.tapestry5.beaneditor.BeanModel;
-import org.apache.tapestry5.beaneditor.Validate;
-import org.apache.tapestry5.corelib.components.Zone;
-import org.apache.tapestry5.func.*;
-import org.apache.tapestry5.internal.PageCatalogTotals;
-import org.apache.tapestry5.internal.services.ComponentInstantiatorSource;
-import org.apache.tapestry5.internal.services.PageSource;
-import org.apache.tapestry5.internal.structure.Page;
-import org.apache.tapestry5.ioc.Messages;
-import org.apache.tapestry5.ioc.OperationTracker;
-import org.apache.tapestry5.ioc.annotations.Inject;
-import org.apache.tapestry5.ioc.annotations.Symbol;
-import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
-import org.apache.tapestry5.ioc.internal.util.InternalUtils;
-import org.apache.tapestry5.services.BeanModelSource;
-import org.apache.tapestry5.services.ComponentClassResolver;
-import org.apache.tapestry5.services.pageload.ComponentResourceSelector;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Lists out the currently loaded pages, using a {@link org.apache.tapestry5.corelib.components.Grid}.
- * Provides an option to force all pages to be loaded. In development mode, includes an option to clear the page cache.
- */
-@ContentType("text/html")
-@WhitelistAccessOnly
-public class PageCatalog
-{
-
-    @Property
-    private PageCatalogTotals totals;
-
-    @Property
-    @Inject
-    @Symbol(SymbolConstants.PRODUCTION_MODE)
-    private boolean productionMode;
-
-    @Inject
-    private PageSource pageSource;
-
-    @Inject
-    private ComponentResourceSelector selector;
-
-    @Inject
-    private ComponentClassResolver resolver;
-
-    @Inject
-    private AlertManager alertManager;
-
-    @Property
-    private Page page;
-
-    @InjectComponent
-    private Zone pagesZone;
-
-    @Persist
-    private Set<String> failures;
-
-    @Property
-    @Validate("required")
-    @Persist
-    private String pageName;
-
-    @Inject
-    private OperationTracker operationTracker;
-
-    @Inject
-    private ComponentInstantiatorSource componentInstantiatorSource;
-
-    @Inject
-    private BeanModelSource beanModelSource;
-
-    @Inject
-    private Messages messages;
-
-    @Property
-    public static BeanModel<Page> model;
-
-    void pageLoaded()
-    {
-        model = beanModelSource.createDisplayModel(Page.class, messages);
-
-        model.addExpression("selector", "selector.toString()");
-        model.addExpression("assemblyTime", "stats.assemblyTime");
-        model.addExpression("componentCount", "stats.componentCount");
-        model.addExpression("weight", "stats.weight");
-
-        model.reorder("name", "selector", "assemblyTime", "componentCount", "weight");
-    }
-
-    public void onRecomputeTotals()
-    {
-        totals = new PageCatalogTotals();
-
-        Flow<Page> pages = F.flow(getPages());
-
-        totals.loadedPages = pages.count();
-        totals.definedPages = getPageNames().size();
-        totals.uniquePageNames = pages.map(new Mapper<Page, String>()
-        {
-            public String map(Page element)
-            {
-                return element.getName();
-            }
-        }).toSet().size();
-
-        totals.components = pages.reduce(new Reducer<Integer, Page>()
-        {
-            public Integer reduce(Integer accumulator, Page element)
-            {
-                return accumulator + element.getStats().componentCount;
-            }
-        }, 0);
-
-        Set<String> selectorIds = pages.map(new Mapper<Page, String>()
-        {
-            public String map(Page element)
-            {
-                return element.getSelector().toShortString();
-            }
-        }).toSet();
-
-        totals.selectors = InternalUtils.joinSorted(selectorIds);
-    }
-
-    public List<String> getPageNames()
-    {
-        return resolver.getPageNames();
-    }
-
-    public Collection<Page> getPages()
-    {
-        return pageSource.getAllPages();
-    }
-
-    Object onActionFromReloadClasses()
-    {
-        if (productionMode)
-        {
-            alertManager.error("Forcing a class reload is only allowed when executing in development mode.");
-            return null;
-        }
-
-        pageName = null;
-
-        componentInstantiatorSource.forceComponentInvalidation();
-
-        alertManager.info("Forced a component class reload.");
-
-        return pagesZone.getBody();
-    }
-
-    Object onSuccessFromSinglePageLoad()
-    {
-        boolean found = !F.flow(getPages()).filter(new Predicate<Page>()
-        {
-            public boolean accept(Page element)
-            {
-                return element.getName().equals(pageName) && element.getSelector().equals(selector);
-            }
-        }).isEmpty();
-
-        if (found)
-        {
-            alertManager.warn(String.format("Page %s has already been loaded for '%s'.",
-                    pageName, selector.toShortString()));
-            return null;
-        }
-
-        long startTime = System.currentTimeMillis();
-
-
-        // Load the page now (may cause an exception).
-
-        pageSource.getPage(pageName);
-
-
-        alertManager.info(String.format("Loaded page %s for selector '%s' (in %,d ms).", pageName,
-                selector.toShortString(), System.currentTimeMillis() - startTime));
-
-        return pagesZone.getBody();
-    }
-
-    private class PageLoadData
-    {
-        int loadedCount;
-        RuntimeException fail;
-        boolean someFail;
-    }
-
-    Object onActionFromForceLoad()
-    {
-        if (failures == null)
-        {
-            failures = CollectionFactory.newSet();
-        }
-
-        long startTime = System.currentTimeMillis();
-
-        final Collection<Page> initialPages = getPages();
-
-        final PageLoadData data = new PageLoadData();
-
-        for (final String name : resolver.getPageNames())
-        {
-            if (failures.contains(name))
-            {
-                alertManager.warn(String.format("Skipping page %s due to prior load failure.", name));
-                data.someFail = true;
-                continue;
-            }
-
-            operationTracker.run("Loading page " + name, new Runnable()
-            {
-                public void run()
-                {
-                    try
-                    {
-                        Page newPage = pageSource.getPage(name);
-
-                        if (!initialPages.contains(newPage))
-                        {
-                            data.loadedCount++;
-                        }
-                    } catch (RuntimeException ex)
-                    {
-                        alertManager.error(String.format("Page %s failed to load.", name));
-                        failures.add(name);
-
-                        if (data.fail == null)
-                        {
-                            pageName = name;
-                            data.fail = ex;
-                        }
-                    }
-                }
-            });
-
-            if (data.fail != null)
-            {
-                break;
-            }
-        }
-
-        alertManager.info(String.format("Loaded %,d new pages for selector '%s' (in %,d ms).", data.loadedCount,
-                selector.toShortString(), System.currentTimeMillis() - startTime));
-
-        if (data.someFail)
-        {
-            alertManager.warn("Clear the cache to reset the list of failed pages.");
-        }
-
-        if (data.fail != null)
-        {
-            throw data.fail;
-        }
-
-        return pagesZone.getBody();
-    }
-
-    Object onActionFromClearCache()
-    {
-        if (productionMode)
-        {
-            alertManager.error("Clearing the cache is only allowed in development mode.");
-            return null;
-        }
-
-        pageSource.clearCache();
-
-        failures = null;
-
-        alertManager.info("Page cache cleared.");
-
-        return pagesZone.getBody();
-    }
-
-    Object onActionFromRunGC()
-    {
-        if (productionMode)
-        {
-            alertManager.error("Executing a garbage collection is only allowed in development mode.");
-            return null;
-        }
-
-        Runtime runtime = Runtime.getRuntime();
-
-        long initialFreeMemory = runtime.freeMemory();
-
-        runtime.gc();
-
-        long delta = runtime.freeMemory() - initialFreeMemory;
-
-        alertManager.info(String.format("Garbage collection freed %,.2f Kb of memory.",
-                ((double) delta) / 1024.0d));
-
-        return pagesZone.getBody();
-    }
-
-    public String formatElapsed(double millis)
-    {
-        return String.format("%,.3f ms", millis);
-    }
-}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/ServiceStatus.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/ServiceStatus.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/ServiceStatus.java
deleted file mode 100644
index 5e7be61..0000000
--- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/ServiceStatus.java
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2007, 2008 The Apache Software Foundation
-//
-// Licensed 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.tapestry5.corelib.pages;
-
-import org.apache.tapestry5.SymbolConstants;
-import org.apache.tapestry5.annotations.ContentType;
-import org.apache.tapestry5.annotations.Import;
-import org.apache.tapestry5.annotations.Property;
-import org.apache.tapestry5.annotations.WhitelistAccessOnly;
-import org.apache.tapestry5.beaneditor.BeanModel;
-import org.apache.tapestry5.ioc.Messages;
-import org.apache.tapestry5.ioc.Registry;
-import org.apache.tapestry5.ioc.annotations.Inject;
-import org.apache.tapestry5.ioc.annotations.Symbol;
-import org.apache.tapestry5.ioc.services.ServiceActivity;
-import org.apache.tapestry5.ioc.services.ServiceActivityScoreboard;
-import org.apache.tapestry5.services.BeanModelSource;
-
-import java.util.List;
-
-/**
- * Page used to see the status of all services defined by the {@link Registry}.
- * <p/>
- * TODO: Add filters to control which services are displayed
- */
-@ContentType("text/html")
-@Import(stylesheet = "ServiceStatus.css")
-@WhitelistAccessOnly
-public class ServiceStatus
-{
-    @Inject
-    private ServiceActivityScoreboard scoreboard;
-
-    @Property
-    private List<ServiceActivity> activity;
-
-    @Property
-    private ServiceActivity row;
-
-    @Inject
-    private BeanModelSource source;
-
-    @Property
-    private final BeanModel model;
-
-    @Inject
-    private Messages messages;
-
-    @Property
-    @Inject
-    @Symbol(SymbolConstants.PRODUCTION_MODE)
-    private boolean productionMode;
-
-    {
-        model = source.createDisplayModel(ServiceActivity.class, messages);
-
-        model.addEmpty("serviceInterface");
-
-        // There's no line number information for interfaces, so we'll reorder the
-        // proprieties manually.
-
-        model.reorder("serviceId", "serviceInterface", "scope", "status");
-    }
-
-    void setupRender()
-    {
-        activity = scoreboard.getServiceActivity();
-    }
-}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/T5Dashboard.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/T5Dashboard.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/T5Dashboard.java
new file mode 100644
index 0000000..3a54b30
--- /dev/null
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/T5Dashboard.java
@@ -0,0 +1,81 @@
+// Copyright 2013 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.corelib.pages;
+
+import org.apache.tapestry5.Block;
+import org.apache.tapestry5.SymbolConstants;
+import org.apache.tapestry5.annotations.ContentType;
+import org.apache.tapestry5.annotations.Import;
+import org.apache.tapestry5.annotations.Property;
+import org.apache.tapestry5.annotations.WhitelistAccessOnly;
+import org.apache.tapestry5.ioc.annotations.Inject;
+import org.apache.tapestry5.ioc.annotations.Symbol;
+import org.apache.tapestry5.services.dashboard.DashboardManager;
+
+/**
+ * @see org.apache.tapestry5.services.dashboard.DashboardManager
+ * @since 5.4
+ */
+@WhitelistAccessOnly
+@ContentType("text/html")
+@Import(stack = "core", stylesheet = "dashboard.css")
+public class T5Dashboard
+{
+    @Inject
+    @Symbol(SymbolConstants.TAPESTRY_VERSION)
+    @Property
+    private String frameworkVersion;
+
+    @Property
+    @Inject
+    @Symbol(SymbolConstants.PRODUCTION_MODE)
+    private boolean productionMode;
+
+    @Inject
+    @Property
+    private DashboardManager dashboardManager;
+
+    @Property
+    private String tabName;
+
+    private String activeTab;
+
+    public String getTabClass()
+    {
+        return tabName.equalsIgnoreCase(activeTab) ? "active" : null;
+    }
+
+    public Block getContent()
+    {
+        return dashboardManager.getTabContent(activeTab);
+    }
+
+    void onActivate()
+    {
+        activeTab = dashboardManager.getTabNames().get(0);
+    }
+
+    boolean onActivate(String tabName)
+    {
+        activeTab = tabName;
+
+        return true;
+    }
+
+    String onPassivate()
+    {
+        return activeTab;
+    }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/main/java/org/apache/tapestry5/internal/PageCatalogTotals.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/PageCatalogTotals.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/PageCatalogTotals.java
index b71e4d6..0a6e2c1 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/PageCatalogTotals.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/PageCatalogTotals.java
@@ -1,4 +1,4 @@
-// Copyright 2011 The Apache Software Foundation
+// Copyright 2011, 2013 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@ package org.apache.tapestry5.internal;
 import org.apache.tapestry5.beaneditor.ReorderProperties;
 
 /**
- * @see org.apache.tapestry5.corelib.pages.PageCatalog
+ * @see org.apache.tapestry5.internal.t5internal.pages.PageCatalog
  */
 @ReorderProperties("definedPages,loadedPages,uniquePageNames,selectors,components")
 public class PageCatalogTotals

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetSourceImpl.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetSourceImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetSourceImpl.java
index 0994ff5..ac83ddb 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetSourceImpl.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetSourceImpl.java
@@ -116,7 +116,7 @@ public class AssetSourceImpl extends LockSupport implements AssetSource
 
     public Asset getComponentAsset(ComponentResources resources, String path)
     {
-        assert resources != null;
+            assert resources != null;
 
         assert InternalUtils.isNonBlank(path);
 

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageSource.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageSource.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageSource.java
index eefd0b6..0bdc042 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageSource.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageSource.java
@@ -1,4 +1,4 @@
-// Copyright 2010, 2011 The Apache Software Foundation
+// Copyright 2010-2013 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -53,7 +53,7 @@ public interface PageSource
      * garbage collector, and may include the same page loaded for different {@link ComponentResourceSelector}s. This is needed
      * for reporting purposes only.
      *
-     * @see org.apache.tapestry5.corelib.pages.PageCatalog
+     * @see org.apache.tapestry5.internal.t5internal.pages.PageCatalog
      * @since 5.3
      */
     Set<Page> getAllPages();

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/dashboard/DashboardManagerImpl.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/dashboard/DashboardManagerImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/dashboard/DashboardManagerImpl.java
new file mode 100644
index 0000000..9b6413f
--- /dev/null
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/dashboard/DashboardManagerImpl.java
@@ -0,0 +1,63 @@
+// Copyright 2013 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.internal.services.dashboard;
+
+import org.apache.tapestry5.Block;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry5.runtime.Component;
+import org.apache.tapestry5.services.ComponentSource;
+import org.apache.tapestry5.services.dashboard.DashboardManager;
+import org.apache.tapestry5.services.dashboard.DashboardTab;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+public class DashboardManagerImpl implements DashboardManager
+{
+    private final ComponentSource componentSource;
+
+    private final List<String> tabNames;
+
+    private final Map<String, String> tab2PageName = CollectionFactory.newCaseInsensitiveMap();
+
+    public DashboardManagerImpl(ComponentSource componentSource, List<DashboardTab> tabs)
+    {
+        this.componentSource = componentSource;
+
+        List<String> tabNames = CollectionFactory.newList();
+
+        for (DashboardTab tab : tabs)
+        {
+            tabNames.add(tab.tabName);
+
+            tab2PageName.put(tab.tabName, tab.pageName);
+        }
+
+        this.tabNames = Collections.unmodifiableList(tabNames);
+    }
+
+    public List<String> getTabNames()
+    {
+        return tabNames;
+    }
+
+    public Block getTabContent(String tabName)
+    {
+        Component page = componentSource.getPage(tab2PageName.get(tabName));
+
+        return page.getComponentResources().getBlock("content");
+    }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/main/java/org/apache/tapestry5/internal/t5internal/components/InternalLayout.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/t5internal/components/InternalLayout.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/t5internal/components/InternalLayout.java
index ec58c40..8862655 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/t5internal/components/InternalLayout.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/t5internal/components/InternalLayout.java
@@ -9,7 +9,7 @@ import org.apache.tapestry5.ioc.annotations.Inject;
 import org.apache.tapestry5.ioc.annotations.Symbol;
 
 /**
- * A default layout for a number of internal pages in Tapestry, such as {@link org.apache.tapestry5.corelib.pages.ServiceStatus} and {@link org.apache.tapestry5.corelib.pages.PageCatalog}.
+ * A default layout for a number of internal pages in Tapestry, such as {@link org.apache.tapestry5.internal.t5internal.pages.ServiceStatus} and {@link org.apache.tapestry5.internal.t5internal.pages.PageCatalog}.
  * <strong>This component is not intended for use in user applications, and may change at any time.</strong>
  *
  * @tapestrydoc

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/main/java/org/apache/tapestry5/internal/t5internal/pages/PageCatalog.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/t5internal/pages/PageCatalog.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/t5internal/pages/PageCatalog.java
new file mode 100644
index 0000000..3f4b31c
--- /dev/null
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/t5internal/pages/PageCatalog.java
@@ -0,0 +1,318 @@
+// Copyright 2011-2013 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.internal.t5internal.pages;
+
+import org.apache.tapestry5.SymbolConstants;
+import org.apache.tapestry5.alerts.AlertManager;
+import org.apache.tapestry5.annotations.*;
+import org.apache.tapestry5.beaneditor.BeanModel;
+import org.apache.tapestry5.beaneditor.Validate;
+import org.apache.tapestry5.corelib.components.Zone;
+import org.apache.tapestry5.func.*;
+import org.apache.tapestry5.internal.PageCatalogTotals;
+import org.apache.tapestry5.internal.services.ComponentInstantiatorSource;
+import org.apache.tapestry5.internal.services.PageSource;
+import org.apache.tapestry5.internal.structure.Page;
+import org.apache.tapestry5.ioc.Messages;
+import org.apache.tapestry5.ioc.OperationTracker;
+import org.apache.tapestry5.ioc.annotations.Inject;
+import org.apache.tapestry5.ioc.annotations.Symbol;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+import org.apache.tapestry5.services.BeanModelSource;
+import org.apache.tapestry5.services.ComponentClassResolver;
+import org.apache.tapestry5.services.pageload.ComponentResourceSelector;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Lists out the currently loaded pages, using a {@link org.apache.tapestry5.corelib.components.Grid}.
+ * Provides an option to force all pages to be loaded. In development mode, includes an option to clear the page cache.
+ */
+@WhitelistAccessOnly
+public class PageCatalog
+{
+
+    @Property
+    private PageCatalogTotals totals;
+
+    @Property
+    @Inject
+    @Symbol(SymbolConstants.PRODUCTION_MODE)
+    private boolean productionMode;
+
+    @Inject
+    private PageSource pageSource;
+
+    @Inject
+    private ComponentResourceSelector selector;
+
+    @Inject
+    private ComponentClassResolver resolver;
+
+    @Inject
+    private AlertManager alertManager;
+
+    @Property
+    private Page page;
+
+    @InjectComponent
+    private Zone pagesZone;
+
+    @Persist
+    private Set<String> failures;
+
+    @Property
+    @Validate("required")
+    @Persist
+    private String pageName;
+
+    @Inject
+    private OperationTracker operationTracker;
+
+    @Inject
+    private ComponentInstantiatorSource componentInstantiatorSource;
+
+    @Inject
+    private BeanModelSource beanModelSource;
+
+    @Inject
+    private Messages messages;
+
+    @Property
+    public static BeanModel<Page> model;
+
+    void pageLoaded()
+    {
+        model = beanModelSource.createDisplayModel(Page.class, messages);
+
+        model.addExpression("selector", "selector.toString()");
+        model.addExpression("assemblyTime", "stats.assemblyTime");
+        model.addExpression("componentCount", "stats.componentCount");
+        model.addExpression("weight", "stats.weight");
+
+        model.reorder("name", "selector", "assemblyTime", "componentCount", "weight");
+    }
+
+    public void onRecomputeTotals()
+    {
+        totals = new PageCatalogTotals();
+
+        Flow<Page> pages = F.flow(getPages());
+
+        totals.loadedPages = pages.count();
+        totals.definedPages = getPageNames().size();
+        totals.uniquePageNames = pages.map(new Mapper<Page, String>()
+        {
+            public String map(Page element)
+            {
+                return element.getName();
+            }
+        }).toSet().size();
+
+        totals.components = pages.reduce(new Reducer<Integer, Page>()
+        {
+            public Integer reduce(Integer accumulator, Page element)
+            {
+                return accumulator + element.getStats().componentCount;
+            }
+        }, 0);
+
+        Set<String> selectorIds = pages.map(new Mapper<Page, String>()
+        {
+            public String map(Page element)
+            {
+                return element.getSelector().toShortString();
+            }
+        }).toSet();
+
+        totals.selectors = InternalUtils.joinSorted(selectorIds);
+    }
+
+    public List<String> getPageNames()
+    {
+        return resolver.getPageNames();
+    }
+
+    public Collection<Page> getPages()
+    {
+        return pageSource.getAllPages();
+    }
+
+    Object onActionFromReloadClasses()
+    {
+        if (productionMode)
+        {
+            alertManager.error("Forcing a class reload is only allowed when executing in development mode.");
+            return null;
+        }
+
+        pageName = null;
+
+        componentInstantiatorSource.forceComponentInvalidation();
+
+        alertManager.info("Forced a component class reload.");
+
+        return pagesZone.getBody();
+    }
+
+    Object onSuccessFromSinglePageLoad()
+    {
+        boolean found = !F.flow(getPages()).filter(new Predicate<Page>()
+        {
+            public boolean accept(Page element)
+            {
+                return element.getName().equals(pageName) && element.getSelector().equals(selector);
+            }
+        }).isEmpty();
+
+        if (found)
+        {
+            alertManager.warn(String.format("Page %s has already been loaded for '%s'.",
+                    pageName, selector.toShortString()));
+            return null;
+        }
+
+        long startTime = System.currentTimeMillis();
+
+
+        // Load the page now (may cause an exception).
+
+        pageSource.getPage(pageName);
+
+
+        alertManager.info(String.format("Loaded page %s for selector '%s' (in %,d ms).", pageName,
+                selector.toShortString(), System.currentTimeMillis() - startTime));
+
+        return pagesZone.getBody();
+    }
+
+    private class PageLoadData
+    {
+        int loadedCount;
+        RuntimeException fail;
+        boolean someFail;
+    }
+
+    Object onActionFromForceLoad()
+    {
+        if (failures == null)
+        {
+            failures = CollectionFactory.newSet();
+        }
+
+        long startTime = System.currentTimeMillis();
+
+        final Collection<Page> initialPages = getPages();
+
+        final PageLoadData data = new PageLoadData();
+
+        for (final String name : resolver.getPageNames())
+        {
+            if (failures.contains(name))
+            {
+                alertManager.warn(String.format("Skipping page %s due to prior load failure.", name));
+                data.someFail = true;
+                continue;
+            }
+
+            operationTracker.run("Loading page " + name, new Runnable()
+            {
+                public void run()
+                {
+                    try
+                    {
+                        Page newPage = pageSource.getPage(name);
+
+                        if (!initialPages.contains(newPage))
+                        {
+                            data.loadedCount++;
+                        }
+                    } catch (RuntimeException ex)
+                    {
+                        alertManager.error(String.format("Page %s failed to load.", name));
+                        failures.add(name);
+
+                        if (data.fail == null)
+                        {
+                            pageName = name;
+                            data.fail = ex;
+                        }
+                    }
+                }
+            });
+
+            if (data.fail != null)
+            {
+                break;
+            }
+        }
+
+        alertManager.info(String.format("Loaded %,d new pages for selector '%s' (in %,d ms).", data.loadedCount,
+                selector.toShortString(), System.currentTimeMillis() - startTime));
+
+        if (data.someFail)
+        {
+            alertManager.warn("Clear the cache to reset the list of failed pages.");
+        }
+
+        if (data.fail != null)
+        {
+            throw data.fail;
+        }
+
+        return pagesZone.getBody();
+    }
+
+    Object onActionFromClearCache()
+    {
+        if (productionMode)
+        {
+            alertManager.error("Clearing the cache is only allowed in development mode.");
+            return null;
+        }
+
+        pageSource.clearCache();
+
+        failures = null;
+
+        alertManager.info("Page cache cleared.");
+
+        return pagesZone.getBody();
+    }
+
+    Object onActionFromRunGC()
+    {
+        Runtime runtime = Runtime.getRuntime();
+
+        long initialFreeMemory = runtime.freeMemory();
+
+        runtime.gc();
+
+        long delta = runtime.freeMemory() - initialFreeMemory;
+
+        alertManager.info(String.format("Garbage collection freed %,.2f Kb of memory.",
+                ((double) delta) / 1024.0d));
+
+        return pagesZone.getBody();
+    }
+
+    public String formatElapsed(double millis)
+    {
+        return String.format("%,.3f ms", millis);
+    }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/main/java/org/apache/tapestry5/internal/t5internal/pages/ServiceStatus.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/t5internal/pages/ServiceStatus.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/t5internal/pages/ServiceStatus.java
new file mode 100644
index 0000000..0434ea8
--- /dev/null
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/t5internal/pages/ServiceStatus.java
@@ -0,0 +1,82 @@
+// Copyright 2007, 2008, 2013 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.internal.t5internal.pages;
+
+import org.apache.tapestry5.SymbolConstants;
+import org.apache.tapestry5.annotations.Cached;
+import org.apache.tapestry5.annotations.Import;
+import org.apache.tapestry5.annotations.Property;
+import org.apache.tapestry5.annotations.WhitelistAccessOnly;
+import org.apache.tapestry5.beaneditor.BeanModel;
+import org.apache.tapestry5.ioc.Messages;
+import org.apache.tapestry5.ioc.Registry;
+import org.apache.tapestry5.ioc.annotations.Inject;
+import org.apache.tapestry5.ioc.annotations.Symbol;
+import org.apache.tapestry5.ioc.services.ServiceActivity;
+import org.apache.tapestry5.ioc.services.ServiceActivityScoreboard;
+import org.apache.tapestry5.services.BeanModelSource;
+
+import java.util.List;
+
+/**
+ * Page used to see the status of all services defined by the {@link Registry}.
+ * <p/>
+ * TODO: Add filters to control which services are displayed
+ */
+@WhitelistAccessOnly
+public class ServiceStatus
+{
+    @Inject
+    private ServiceActivityScoreboard scoreboard;
+
+    @Property
+    private ServiceActivity row;
+
+    @Inject
+    private BeanModelSource source;
+
+    @Property
+    private final BeanModel model;
+
+    @Inject
+    private Messages messages;
+
+    @Property
+    @Inject
+    @Symbol(SymbolConstants.PRODUCTION_MODE)
+    private boolean productionMode;
+
+    {
+        model = source.createDisplayModel(ServiceActivity.class, messages);
+
+        model.addEmpty("serviceInterface");
+
+        // There's no line number information for interfaces, so we'll reorder the
+        // proprieties manually.
+
+        model.reorder("serviceId", "serviceInterface", "scope", "status");
+    }
+
+    @Cached
+    public List<ServiceActivity> getActivity()
+    {
+        return scoreboard.getServiceActivity();
+    }
+
+    @Import(stylesheet = "service-status.css")
+    void onAfterRenderFromServices()
+    {
+    }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java b/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
index c5f5e70..9af3779 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
@@ -73,6 +73,7 @@ import org.apache.tapestry5.runtime.RenderQueue;
 import org.apache.tapestry5.services.ajax.AjaxResponseRenderer;
 import org.apache.tapestry5.services.assets.AssetsModule;
 import org.apache.tapestry5.services.compatibility.CompatibilityModule;
+import org.apache.tapestry5.services.dashboard.DashboardModule;
 import org.apache.tapestry5.services.dynamic.DynamicTemplate;
 import org.apache.tapestry5.services.dynamic.DynamicTemplateParser;
 import org.apache.tapestry5.services.javascript.JavaScriptModule;
@@ -112,7 +113,7 @@ import java.util.regex.Pattern;
  */
 @Marker(Core.class)
 @SubModule(
-        {InternalModule.class, AssetsModule.class, PageLoadModule.class, JavaScriptModule.class, CompatibilityModule.class})
+        {InternalModule.class, AssetsModule.class, PageLoadModule.class, JavaScriptModule.class, CompatibilityModule.class, DashboardModule.class})
 public final class TapestryModule
 {
     private final PipelineBuilder pipelineBuilder;

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/main/java/org/apache/tapestry5/services/dashboard/DashboardManager.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/services/dashboard/DashboardManager.java b/tapestry-core/src/main/java/org/apache/tapestry5/services/dashboard/DashboardManager.java
new file mode 100644
index 0000000..0107da2
--- /dev/null
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/services/dashboard/DashboardManager.java
@@ -0,0 +1,33 @@
+// Copyright 2013 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.services.dashboard;
+
+import org.apache.tapestry5.Block;
+import org.apache.tapestry5.ioc.annotations.UsesOrderedConfiguration;
+
+import java.util.List;
+
+/**
+ * Organizes the tabs on the {@link org.apache.tapestry5.corelib.pages.T5Dashboard} page.
+ *
+ * @since 5.4
+ */
+@UsesOrderedConfiguration(DashboardTab.class)
+public interface DashboardManager
+{
+    List<String> getTabNames();
+
+    Block getTabContent(String tabName);
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/main/java/org/apache/tapestry5/services/dashboard/DashboardModule.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/services/dashboard/DashboardModule.java b/tapestry-core/src/main/java/org/apache/tapestry5/services/dashboard/DashboardModule.java
new file mode 100644
index 0000000..60e5129
--- /dev/null
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/services/dashboard/DashboardModule.java
@@ -0,0 +1,35 @@
+// Copyright 2013 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.services.dashboard;
+
+import org.apache.tapestry5.internal.services.dashboard.DashboardManagerImpl;
+import org.apache.tapestry5.ioc.OrderedConfiguration;
+import org.apache.tapestry5.ioc.ServiceBinder;
+import org.apache.tapestry5.ioc.annotations.Contribute;
+
+public class DashboardModule
+{
+    public static void bind(ServiceBinder binder)
+    {
+        binder.bind(DashboardManager.class, DashboardManagerImpl.class);
+    }
+
+    @Contribute(DashboardManager.class)
+    public static void defaultTabs(OrderedConfiguration<DashboardTab> configuration)
+    {
+        configuration.add("Pages", new DashboardTab("Pages", "t5internal/PageCatalog"));
+        configuration.add("Services", new DashboardTab("Services", "t5internal/ServiceStatus"));
+    }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/main/java/org/apache/tapestry5/services/dashboard/DashboardTab.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/services/dashboard/DashboardTab.java b/tapestry-core/src/main/java/org/apache/tapestry5/services/dashboard/DashboardTab.java
new file mode 100644
index 0000000..a71b425
--- /dev/null
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/services/dashboard/DashboardTab.java
@@ -0,0 +1,44 @@
+// Copyright 2013 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.services.dashboard;
+
+import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+
+/**
+ * Identifies a tab that will be present on the Tapestry Dashboard page.
+ *
+ * @since 5.4
+ */
+public class DashboardTab
+{
+    /**
+     * Title for the tab, to be displayed on the Dashboard page.
+     */
+    public final String tabName;
+
+    /**
+     * Name of Tapestry page that provides the "content" block.
+     */
+    public final String pageName;
+
+    public DashboardTab(String tabName, String pageName)
+    {
+        assert InternalUtils.isNonBlank(tabName);
+        assert InternalUtils.isNonBlank(pageName);
+
+        this.tabName = tabName;
+        this.pageName = pageName;
+    }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/main/java/org/apache/tapestry5/services/dashboard/package-info.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/services/dashboard/package-info.java b/tapestry-core/src/main/java/org/apache/tapestry5/services/dashboard/package-info.java
new file mode 100644
index 0000000..50a3b34
--- /dev/null
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/services/dashboard/package-info.java
@@ -0,0 +1,18 @@
+// Copyright 2013 The Apache Software Foundation
+//
+// Licensed 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.
+
+/**
+ * Support for the built-in "T5Dashboard" page.
+ */
+package org.apache.tapestry5.services.dashboard;

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/main/resources/META-INF/assets/core/ServiceStatus.css
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/resources/META-INF/assets/core/ServiceStatus.css b/tapestry-core/src/main/resources/META-INF/assets/core/ServiceStatus.css
deleted file mode 100644
index 15a1db7..0000000
--- a/tapestry-core/src/main/resources/META-INF/assets/core/ServiceStatus.css
+++ /dev/null
@@ -1,12 +0,0 @@
-TR.DEFINED {
-    color: #666666;
-    font-style: italic;
-}
-
-TR.VIRTUAL {
-    color: blue;
-}
-
-TR.REAL {
-    color: green;
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/main/resources/META-INF/assets/core/dashboard.css
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/resources/META-INF/assets/core/dashboard.css b/tapestry-core/src/main/resources/META-INF/assets/core/dashboard.css
new file mode 100644
index 0000000..c066ca7
--- /dev/null
+++ b/tapestry-core/src/main/resources/META-INF/assets/core/dashboard.css
@@ -0,0 +1,7 @@
+body {
+    padding-top: 40px;
+}
+
+.brand sub {
+    font-size: x-small;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/main/resources/META-INF/assets/t5internal/service-status.css
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/resources/META-INF/assets/t5internal/service-status.css b/tapestry-core/src/main/resources/META-INF/assets/t5internal/service-status.css
new file mode 100644
index 0000000..15a1db7
--- /dev/null
+++ b/tapestry-core/src/main/resources/META-INF/assets/t5internal/service-status.css
@@ -0,0 +1,12 @@
+TR.DEFINED {
+    color: #666666;
+    font-style: italic;
+}
+
+TR.VIRTUAL {
+    color: blue;
+}
+
+TR.REAL {
+    color: green;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/PageCatalog.properties
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/PageCatalog.properties b/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/PageCatalog.properties
deleted file mode 100644
index d1b1307..0000000
--- a/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/PageCatalog.properties
+++ /dev/null
@@ -1,3 +0,0 @@
-loadedpages-label=Pages in Cache
-components-label=Total # of Components
-selectors-label=Active Selectors

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/PageCatalog.tml
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/PageCatalog.tml b/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/PageCatalog.tml
deleted file mode 100644
index d425f2b..0000000
--- a/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/PageCatalog.tml
+++ /dev/null
@@ -1,89 +0,0 @@
-<i:internallayout title="Page Catalog" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"
-                  xmlns:p="tapestry:parameter"
-                  xmlns:i="tapestry-library:t5internal">
-
-    <p:leftnav>
-
-        <ul class="nav nav-pills nav-stacked">
-            <li>
-                <t:pagelink page="pagecatalog">refresh this page</t:pagelink>
-            </li>
-            <li>
-                <t:actionlink t:id="forceLoad" zone="pages">load all pages</t:actionlink>
-            </li>
-            <t:if test="! productionMode">
-                <li>
-                    <t:actionlink t:id="clearCache" zone="pages">clear the cache</t:actionlink>
-                </li>
-                <li>
-                    <t:actionlink t:id="reloadClasses" zone="pages">reload component classes</t:actionlink>
-                </li>
-                <li>
-                    <t:actionlink t:id="runGC" zone="pages">Run the GC</t:actionlink>
-                </li>
-            </t:if>
-        </ul>
-
-    </p:leftnav>
-
-
-    <t:zone t:id="pagesZone" id="pages">
-
-        <p xml:space="preserve">
-            This page provides a list of pages currently loaded in the application.
-        </p>
-
-        <t:trigger event="recomputeTotals"/>
-
-        <t:beandisplay t:id="totals"/>
-
-        <t:grid source="pages" row="page" model="model">
-            <p:assemblyTimeCell>
-                ${formatElapsed(page.stats.assemblyTime)}
-            </p:assemblyTimeCell>
-            <p:selectorCell>
-                ${page.selector.toShortString()}
-            </p:selectorCell>
-            <p:empty>
-                <p><em>There are no pages in the page cache. This can only occur immediately after reloading all
-                    component classes or clearing the cache.</em></p>
-            </p:empty>
-        </t:grid>
-
-    </t:zone>
-
-    <t:form t:id="singlePageLoad" zone="pages" class="form-inline">
-
-            <t:label for="pageName">Load single page:</t:label>
-            <t:select t:id="pageName" model="pageNames"/>
-
-        <input type="submit" value="Load Page" class="btn btn-primary"/>
-
-    </t:form>
-
-    <dl>
-        <dt>Defined Pages</dt>
-        <dd>Number of page classes.</dd>
-        <dt>Pages in Cache</dt>
-        <dd>Number of page instances currently loaded. This may include the same page class for different selectors.
-        </dd>
-        <dt>Unique Page Names</dt>
-        <dd>Number of pages loaded, ignoring selectors.</dd>
-        <dt>Selector</dt>
-        <dd>The locale (plus application-specific other information) for which the page was assembled. A new instance of
-            a Page will be created for each new selector,
-            as needed.
-        </dd>
-        <dt>Assembly Time</dt>
-        <dd>Time to assemble a complete instance of a page, including all sub-components, and all bindings and other
-            connections
-            between them.
-        </dd>
-        <dt>Component Count</dt>
-        <dd>Number of components on the page, including the root component.</dd>
-        <dt>Weight</dt>
-        <dd>Arbitrary number that includes number of components and mixins, template tokens, and other factors.</dd>
-    </dl>
-
-
-</i:internallayout>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/ServiceStatus.properties
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/ServiceStatus.properties b/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/ServiceStatus.properties
deleted file mode 100644
index 58b9e72..0000000
--- a/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/ServiceStatus.properties
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright 2008 The Apache Software Foundation
-#
-# Licensed 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.
-
-service-status-hidden=Detailed service status is only available in development mode.

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/ServiceStatus.tml
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/ServiceStatus.tml b/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/ServiceStatus.tml
deleted file mode 100644
index 43b8295..0000000
--- a/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/ServiceStatus.tml
+++ /dev/null
@@ -1,64 +0,0 @@
-<i:internallayout title="Tapestry IoC Services Status" xml:space="default"
-                  xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd" xmlns:p="tapestry:parameter"
-                  xmlns:i="tapestry-library:t5internal">
-
-    <p><strong>${activity.size()}</strong> services defined in the IoC Registry.</p>
-
-    <p:leftNav>
-        <p>
-            Explanation of status:
-            <dl>
-                <dt>Builtin</dt>
-                <dd>
-                    A fundamental service that exists even before the Registry is
-                    created.
-                </dd>
-
-                <dt>Defined</dt>
-                <dd>
-                    The service is defined, but has not yet been referenced.
-                </dd>
-
-                <dt>Virtual</dt>
-                <dd>
-                    The service has been referenced (usually for injection into
-                    another service) but has not yet been
-                    <em>realized</em>
-                    into an instantiated service. Realization occurs with the
-                    first method invocation on the proxy.
-                </dd>
-
-                <dt>Real</dt>
-                <dd>
-                    The service has been realized: instantiated, dependencies
-                    injected, decorated with interceptors and is fully in
-                    operation.
-                </dd>
-
-            </dl>
-        </p>
-    </p:leftNav>
-
-
-    <t:if test="! productionMode">
-
-        <t:grid rowsperpage="100" model="model" rowClass="row.status" source="activity" row="row">
-
-            <p:serviceInterfaceCell>
-                <t:if test="! row.serviceInterface.interface">
-                    <em>Class</em>
-                </t:if>
-                ${row.serviceInterface.name}
-            </p:serviceInterfaceCell>
-
-        </t:grid>
-
-
-        <p:else>
-            <p>
-                <em>${message:service-status-hidden}</em>
-            </p>
-        </p:else>
-    </t:if>
-
-</i:internallayout>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/T5Dashboard.tml
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/T5Dashboard.tml b/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/T5Dashboard.tml
new file mode 100644
index 0000000..7e909f9
--- /dev/null
+++ b/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/pages/T5Dashboard.tml
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
+<head><title>Tapestry 5 Dashboard</title></head>
+<body>
+
+<div class="navbar navbar-fixed-top navbar-inverse">
+    <div class="navbar-inner">
+        <a href="${symbol:tapestry.context-path}/" class="brand">Apache Tapestry 5
+            <sub>${frameworkVersion}
+                <t:if test="!productionMode">
+                    (dev mode)
+                </t:if>
+            </sub>
+        </a>
+        <ul class="nav nav-pills">
+            <t:loop source="dashboardManager.tabNames" value="tabName">
+                <li class="${tabClass}">
+                    <t:pagelink page="core/t5dashboard" context="tabName.toLowerCase()">${tabName}</t:pagelink>
+                </li>
+            </t:loop>
+        </ul>
+    </div>
+</div>
+
+<div class="container-fluid">
+    <t:alerts/>
+    <t:delegate to="content"/>
+</div>
+</body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/main/resources/org/apache/tapestry5/internal/t5internal/pages/PageCatalog.properties
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/resources/org/apache/tapestry5/internal/t5internal/pages/PageCatalog.properties b/tapestry-core/src/main/resources/org/apache/tapestry5/internal/t5internal/pages/PageCatalog.properties
new file mode 100644
index 0000000..d1b1307
--- /dev/null
+++ b/tapestry-core/src/main/resources/org/apache/tapestry5/internal/t5internal/pages/PageCatalog.properties
@@ -0,0 +1,3 @@
+loadedpages-label=Pages in Cache
+components-label=Total # of Components
+selectors-label=Active Selectors

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/main/resources/org/apache/tapestry5/internal/t5internal/pages/PageCatalog.tml
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/resources/org/apache/tapestry5/internal/t5internal/pages/PageCatalog.tml b/tapestry-core/src/main/resources/org/apache/tapestry5/internal/t5internal/pages/PageCatalog.tml
new file mode 100644
index 0000000..771f4a9
--- /dev/null
+++ b/tapestry-core/src/main/resources/org/apache/tapestry5/internal/t5internal/pages/PageCatalog.tml
@@ -0,0 +1,78 @@
+<t:block id="content" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"
+         xmlns:p="tapestry:parameter">
+
+    <t:zone t:id="pagesZone" id="pages">
+
+        <p xml:space="preserve">
+            This page provides a list of pages currently loaded in the application.
+        </p>
+
+        <t:trigger event="recomputeTotals"/>
+
+        <t:beandisplay t:id="totals"/>
+
+        <t:grid source="pages" row="page" model="model">
+            <p:assemblyTimeCell>
+                ${formatElapsed(page.stats.assemblyTime)}
+            </p:assemblyTimeCell>
+            <p:selectorCell>
+                ${page.selector.toShortString()}
+            </p:selectorCell>
+            <p:empty>
+                <p><em>There are no pages in the page cache. This can only occur immediately after reloading all
+                    component classes or clearing the cache.</em></p>
+            </p:empty>
+        </t:grid>
+
+    </t:zone>
+
+
+    <div class="btn-group">
+        <t:actionlink t:id="forceLoad" zone="pages" class="btn">Load all pages</t:actionlink>
+        <t:if test="! productionMode">
+            <t:actionlink t:id="clearCache" zone="pages" class="btn">Clear the cache</t:actionlink>
+            <t:actionlink t:id="reloadClasses" zone="pages" class="btn">Reload component classes</t:actionlink>
+        </t:if>
+        <t:actionlink t:id="runGC" zone="pages" class="btn">Run the GC</t:actionlink>
+    </div>
+
+
+    <ul class="nav nav-pills nav-stacked">
+        <li>
+        </li>
+    </ul>
+
+    <t:form t:id="singlePageLoad" zone="pages" class="form-inline">
+
+        <t:label for="pageName">Load single page:</t:label>
+        <t:select t:id="pageName" model="pageNames"/>
+
+        <input type="submit" value="Load Page" class="btn btn-primary"/>
+
+    </t:form>
+
+    <dl>
+        <dt>Defined Pages</dt>
+        <dd>Number of page classes.</dd>
+        <dt>Pages in Cache</dt>
+        <dd>Number of page instances currently loaded. This may include the same page class for different selectors.
+        </dd>
+        <dt>Unique Page Names</dt>
+        <dd>Number of pages loaded, ignoring selectors.</dd>
+        <dt>Selector</dt>
+        <dd>The locale (plus application-specific other information) for which the page was assembled. A new instance of
+            a Page will be created for each new selector,
+            as needed.
+        </dd>
+        <dt>Assembly Time</dt>
+        <dd>Time to assemble a complete instance of a page, including all sub-components, and all bindings and other
+            connections
+            between them.
+        </dd>
+        <dt>Component Count</dt>
+        <dd>Number of components on the page, including the root component.</dd>
+        <dt>Weight</dt>
+        <dd>Arbitrary number that includes number of components and mixins, template tokens, and other factors.</dd>
+    </dl>
+
+</t:block>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/main/resources/org/apache/tapestry5/internal/t5internal/pages/ServiceStatus.properties
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/resources/org/apache/tapestry5/internal/t5internal/pages/ServiceStatus.properties b/tapestry-core/src/main/resources/org/apache/tapestry5/internal/t5internal/pages/ServiceStatus.properties
new file mode 100644
index 0000000..c6d4035
--- /dev/null
+++ b/tapestry-core/src/main/resources/org/apache/tapestry5/internal/t5internal/pages/ServiceStatus.properties
@@ -0,0 +1,15 @@
+# Copyright 2008, 2013 The Apache Software Foundation
+#
+# Licensed 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.
+
+service-status-hidden=Detailed service status is only available in development mode.

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/main/resources/org/apache/tapestry5/internal/t5internal/pages/ServiceStatus.tml
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/resources/org/apache/tapestry5/internal/t5internal/pages/ServiceStatus.tml b/tapestry-core/src/main/resources/org/apache/tapestry5/internal/t5internal/pages/ServiceStatus.tml
new file mode 100644
index 0000000..ed782c1
--- /dev/null
+++ b/tapestry-core/src/main/resources/org/apache/tapestry5/internal/t5internal/pages/ServiceStatus.tml
@@ -0,0 +1,59 @@
+<t:block id="content"
+         xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd" xmlns:p="tapestry:parameter">
+
+    <p><strong>${activity.size()}</strong> services defined in the IoC Registry.</p>
+
+    <t:if test="! productionMode">
+
+        <t:grid t:id="services" inplace="true" rowsperpage="100" model="model" rowClass="row.status" source="activity"
+                row="row" t:mixins="renderNotification">
+
+            <p:serviceInterfaceCell>
+                <t:if test="! row.serviceInterface.interface">
+                    <em>Class</em>
+                </t:if>
+                ${row.serviceInterface.name}
+            </p:serviceInterfaceCell>
+
+        </t:grid>
+
+
+        <p:else>
+            <p>
+                <em>${message:service-status-hidden}</em>
+            </p>
+        </p:else>
+    </t:if>
+
+
+    <dl>
+        <dt>Builtin</dt>
+        <dd>
+            A fundamental service that exists even before the Registry is
+            created.
+        </dd>
+
+        <dt>Defined</dt>
+        <dd>
+            The service is defined, but has not yet been referenced.
+        </dd>
+
+        <dt>Virtual</dt>
+        <dd>
+            The service has been referenced (usually for injection into
+            another service) but has not yet been
+            <em>realized</em>
+            into an instantiated service. Realization occurs with the
+            first method invocation on the proxy.
+        </dd>
+
+        <dt>Real</dt>
+        <dd>
+            The service has been realized: instantiated, dependencies
+            injected, decorated with interceptors and is fully in
+            operation.
+        </dd>
+
+    </dl>
+
+</t:block>

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/app3/PageCatalogTests.groovy
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/app3/PageCatalogTests.groovy b/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/app3/PageCatalogTests.groovy
index c53dbbf..7243357 100644
--- a/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/app3/PageCatalogTests.groovy
+++ b/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/app3/PageCatalogTests.groovy
@@ -9,15 +9,11 @@ class PageCatalogTests extends TapestryCoreTestCase
     @Test
     void load_page_catalog_page()
     {
-        def title = "Tapestry 5: Page Catalog"
-
-        open("${baseURL}pagecatalog")
-
-        assertTitle title
+        open("${baseURL}t5dashboard/pages")
 
         waitForPageInitialized()
 
-        click "link=clear the cache"
+        click "link=Clear the cache"
 
         sleep 1000
 
@@ -29,7 +25,7 @@ class PageCatalogTests extends TapestryCoreTestCase
 
         assertTextPresent "Garbage collection freed"
 
-        click "link=load all pages"
+        click "link=Load all pages"
 
         // Ignore any errors that occur.
     }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/CoreBehaviorsTests.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/CoreBehaviorsTests.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/CoreBehaviorsTests.java
index b6a8786..51c1683 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/CoreBehaviorsTests.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/CoreBehaviorsTests.java
@@ -1,4 +1,4 @@
-// Copyright 2009, 2010, 2011, 2012 The Apache Software Foundation
+// Copyright 2009-2013 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -700,9 +700,9 @@ public class CoreBehaviorsTests extends TapestryCoreTestCase
     @Test
     public void services_status()
     {
-        open(getBaseURL() + "servicestatus");
+        open(getBaseURL() + "t5dashboard/services");
 
-        assertTextPresent("Tapestry IoC Services Status");
+        assertTextPresent("services defined in the IoC Registry");
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-hibernate/src/main/java/org/apache/tapestry5/hibernate/HibernateModule.java
----------------------------------------------------------------------
diff --git a/tapestry-hibernate/src/main/java/org/apache/tapestry5/hibernate/HibernateModule.java b/tapestry-hibernate/src/main/java/org/apache/tapestry5/hibernate/HibernateModule.java
index b121c81..bb7c705 100644
--- a/tapestry-hibernate/src/main/java/org/apache/tapestry5/hibernate/HibernateModule.java
+++ b/tapestry-hibernate/src/main/java/org/apache/tapestry5/hibernate/HibernateModule.java
@@ -1,4 +1,4 @@
-// Copyright 2007, 2008, 2009, 2011 The Apache Software Foundation
+// Copyright 2007-2013 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -37,6 +37,8 @@ import org.apache.tapestry5.services.ApplicationStatePersistenceStrategy;
 import org.apache.tapestry5.services.LibraryMapping;
 import org.apache.tapestry5.services.PersistentFieldStrategy;
 import org.apache.tapestry5.services.ValueEncoderFactory;
+import org.apache.tapestry5.services.dashboard.DashboardManager;
+import org.apache.tapestry5.services.dashboard.DashboardTab;
 import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
 import org.hibernate.Session;
 import org.hibernate.mapping.PersistentClass;
@@ -187,7 +189,12 @@ public class HibernateModule
      */
     public static void contributeComponentClassResolver(Configuration<LibraryMapping> configuration)
     {
-        configuration.add(new LibraryMapping("hibernate", "org.apache.tapestry5.hibernate"));
+        configuration.add(new LibraryMapping("t5internal", "org.apache.tapestry5.hibernate.t5internal"));
     }
 
+    @Contribute(DashboardManager.class)
+    public static void provideHibernateDashboardTab(OrderedConfiguration<DashboardTab> configuration)
+    {
+        configuration.add("HibernateStatistics", new DashboardTab("Hibernate", "t5internal/HibernateStatistics"));
+    }
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-hibernate/src/main/java/org/apache/tapestry5/hibernate/pages/Statistics.java
----------------------------------------------------------------------
diff --git a/tapestry-hibernate/src/main/java/org/apache/tapestry5/hibernate/pages/Statistics.java b/tapestry-hibernate/src/main/java/org/apache/tapestry5/hibernate/pages/Statistics.java
deleted file mode 100644
index d432caf..0000000
--- a/tapestry-hibernate/src/main/java/org/apache/tapestry5/hibernate/pages/Statistics.java
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2009, 2011 The Apache Software Foundation
-//
-// Licensed 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.tapestry5.hibernate.pages;
-
-import java.util.Collection;
-
-import org.apache.tapestry5.SymbolConstants;
-import org.apache.tapestry5.annotations.ContentType;
-import org.apache.tapestry5.annotations.Property;
-import org.apache.tapestry5.annotations.WhitelistAccessOnly;
-import org.apache.tapestry5.ioc.annotations.Inject;
-import org.apache.tapestry5.ioc.annotations.Symbol;
-import org.hibernate.Session;
-import org.hibernate.metadata.ClassMetadata;
-import org.hibernate.stat.CollectionStatistics;
-import org.hibernate.stat.EntityStatistics;
-import org.hibernate.stat.QueryStatistics;
-import org.hibernate.stat.SecondLevelCacheStatistics;
-
-/**
- * Page used to see the Hibernate statistics.
- * 
- * @since 5.1.0.2
- *
- */
-@ContentType("text/html")
-@WhitelistAccessOnly
-public class Statistics {
-   @Inject
-   private Session session;
-
-   @Property
-   @Inject
-   @Symbol(SymbolConstants.PRODUCTION_MODE)
-   private boolean productionMode;
-
-   @Property
-   private String currentEntityName;
-
-   @Property
-   private String currentCollectionRoleName;
-
-   @Property
-   private String currentQuery;
-
-   @Property
-   private String currentSecondLevelCacheRegionName;
-
-   @Property
-   private org.hibernate.stat.Statistics statistics;
-
-   void onActivate() {
-     this.statistics = this.session.getSessionFactory().getStatistics();
-   }
-
-   @SuppressWarnings("unchecked")
-   public Collection<ClassMetadata> getAllClassMetadata() {
-     return this.session.getSessionFactory().getAllClassMetadata().values
-();
-   }
-
-   public EntityStatistics getEntityStatistics() {
-     return this.statistics.getEntityStatistics(this.currentEntityName);
-   }
-
-   public CollectionStatistics getCollectionStatistics() {
-     return this.statistics
-           .getCollectionStatistics(this.currentCollectionRoleName);
-   }
-
-   public QueryStatistics getQueryStatistics() {
-     return this.statistics.getQueryStatistics(this.currentQuery);
-   }
-
-   public SecondLevelCacheStatistics getSecondLevelCacheStatistics() {
-     return this.statistics
-           .getSecondLevelCacheStatistics
-(this.currentSecondLevelCacheRegionName);
-   }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-hibernate/src/main/java/org/apache/tapestry5/hibernate/pages/package-info.java
----------------------------------------------------------------------
diff --git a/tapestry-hibernate/src/main/java/org/apache/tapestry5/hibernate/pages/package-info.java b/tapestry-hibernate/src/main/java/org/apache/tapestry5/hibernate/pages/package-info.java
deleted file mode 100644
index cfda279..0000000
--- a/tapestry-hibernate/src/main/java/org/apache/tapestry5/hibernate/pages/package-info.java
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2012 The Apache Software Foundation
-//
-// Licensed 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.
-
-/**
- * Pages included with the Tapestry-hibernate module
- */
-package org.apache.tapestry5.hibernate.pages;

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-hibernate/src/main/java/org/apache/tapestry5/hibernate/t5internal/pages/HibernateStatistics.java
----------------------------------------------------------------------
diff --git a/tapestry-hibernate/src/main/java/org/apache/tapestry5/hibernate/t5internal/pages/HibernateStatistics.java b/tapestry-hibernate/src/main/java/org/apache/tapestry5/hibernate/t5internal/pages/HibernateStatistics.java
new file mode 100644
index 0000000..0b67517
--- /dev/null
+++ b/tapestry-hibernate/src/main/java/org/apache/tapestry5/hibernate/t5internal/pages/HibernateStatistics.java
@@ -0,0 +1,91 @@
+// Copyright 2009, 2011, 2013 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.hibernate.t5internal.pages;
+
+import org.apache.tapestry5.SymbolConstants;
+import org.apache.tapestry5.annotations.Property;
+import org.apache.tapestry5.annotations.WhitelistAccessOnly;
+import org.apache.tapestry5.ioc.annotations.Inject;
+import org.apache.tapestry5.ioc.annotations.Symbol;
+import org.hibernate.Session;
+import org.hibernate.metadata.ClassMetadata;
+import org.hibernate.stat.CollectionStatistics;
+import org.hibernate.stat.EntityStatistics;
+import org.hibernate.stat.QueryStatistics;
+import org.hibernate.stat.SecondLevelCacheStatistics;
+import org.hibernate.stat.Statistics;
+
+import java.util.Collection;
+
+/**
+ * Page used to see the Hibernate statistics.
+ *
+ * @since 5.1.0.2
+ */
+@WhitelistAccessOnly
+public class HibernateStatistics
+{
+    @Inject
+    private Session session;
+
+    @Property
+    @Inject
+    @Symbol(SymbolConstants.PRODUCTION_MODE)
+    private boolean productionMode;
+
+    @Property
+    private String currentEntityName;
+
+    @Property
+    private String currentCollectionRoleName;
+
+    @Property
+    private String currentQuery;
+
+    @Property
+    private String currentSecondLevelCacheRegionName;
+
+    public Statistics getStatistics()
+    {
+        return session.getSessionFactory().getStatistics();
+    }
+
+
+    @SuppressWarnings("unchecked")
+    public Collection<ClassMetadata> getAllClassMetadata()
+    {
+        return session.getSessionFactory().getAllClassMetadata().values();
+    }
+
+    public EntityStatistics getEntityStatistics()
+    {
+        return getStatistics().getEntityStatistics(currentEntityName);
+    }
+
+    public CollectionStatistics getCollectionStatistics()
+    {
+        return getStatistics().getCollectionStatistics(currentCollectionRoleName);
+    }
+
+    public QueryStatistics getQueryStatistics()
+    {
+        return getStatistics().getQueryStatistics(currentQuery);
+    }
+
+    public SecondLevelCacheStatistics getSecondLevelCacheStatistics()
+    {
+        return getStatistics().getSecondLevelCacheStatistics(currentSecondLevelCacheRegionName);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-hibernate/src/main/java/org/apache/tapestry5/hibernate/t5internal/pages/package-info.java
----------------------------------------------------------------------
diff --git a/tapestry-hibernate/src/main/java/org/apache/tapestry5/hibernate/t5internal/pages/package-info.java b/tapestry-hibernate/src/main/java/org/apache/tapestry5/hibernate/t5internal/pages/package-info.java
new file mode 100644
index 0000000..b18dd60
--- /dev/null
+++ b/tapestry-hibernate/src/main/java/org/apache/tapestry5/hibernate/t5internal/pages/package-info.java
@@ -0,0 +1,19 @@
+// Copyright 2012, 2013 The Apache Software Foundation
+//
+// Licensed 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.
+
+/**
+ * Pages included with the Tapestry-hibernate module; this is used to plug in an additonal "Hibernate"
+ * page into {@link org.apache.tapestry5.corelib.pages.T5Dashboard}.
+ */
+package org.apache.tapestry5.hibernate.t5internal.pages;

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-hibernate/src/main/resources/org/apache/tapestry5/hibernate/pages/Statistics.properties
----------------------------------------------------------------------
diff --git a/tapestry-hibernate/src/main/resources/org/apache/tapestry5/hibernate/pages/Statistics.properties b/tapestry-hibernate/src/main/resources/org/apache/tapestry5/hibernate/pages/Statistics.properties
deleted file mode 100644
index c9fbacf..0000000
--- a/tapestry-hibernate/src/main/resources/org/apache/tapestry5/hibernate/pages/Statistics.properties
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright 2009 The Apache Software Foundation
-#
-# Licensed 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.
-
-hibernate-statistics-hidden=Hibernate statistics are only available in development mode.

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/04bf4d86/tapestry-hibernate/src/main/resources/org/apache/tapestry5/hibernate/pages/Statistics.tml
----------------------------------------------------------------------
diff --git a/tapestry-hibernate/src/main/resources/org/apache/tapestry5/hibernate/pages/Statistics.tml b/tapestry-hibernate/src/main/resources/org/apache/tapestry5/hibernate/pages/Statistics.tml
deleted file mode 100644
index bc57d03..0000000
--- a/tapestry-hibernate/src/main/resources/org/apache/tapestry5/hibernate/pages/Statistics.tml
+++ /dev/null
@@ -1,69 +0,0 @@
-<html xml:space="default"
-	xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
-	<head>
-		<title>Hibernate Statistics</title>
-	</head>
-	<body>
-		<t:unless test="productionMode">
-			<h1>Hibernate Statistics</h1>
-			<ul>
-				<li>
-					<a href="#EntitiesStatistics">
-						Entities Statistics
-					</a>
-				</li>
-				<li>
-					<a href="#CollectionStatistics">
-						Collection Statistics
-					</a>
-				</li>
-				<li>
-					<a href="#QueriesStatistics">Queries Statistics</a>
-				</li>
-				<li>
-					<a href="#SecondLevelCacheStatistics">
-						Second Level Cache Statistics
-					</a>
-				</li>
-			</ul>
-
-			<h1>Session Factory Statistics</h1>
-			<t:beandisplay object="statistics" />
-
-			<a name="EntitiesStatistics" />
-			<h1>Entities Statistics</h1>
-			<t:loop source="statistics.entityNames"
-				value="currentEntityName">
-				<h2>${currentEntityName}</h2>
-				<t:beandisplay object="entityStatistics" />
-			</t:loop>
-
-			<a name="CollectionStatistics" />
-			<h1>Collection Statistics</h1>
-			<t:loop source="statistics.collectionRoleNames"
-				value="currentCollectionRoleName">
-				<h2>${currentCollectionRoleName}</h2>
-				<t:beandisplay object="collectionStatistics" />
-			</t:loop>
-
-			<a name="QueriesStatistics" />
-			<h1>Queries Statistics</h1>
-			<t:loop source="statistics.queries" value="currentQuery">
-				<t:beandisplay object="queryStatistics" />
-			</t:loop>
-
-			<a name="SecondLevelCacheStatistics" />
-			<h1>Second Level Cache Statistics</h1>
-			<t:loop source="statistics.secondLevelCacheRegionNames"
-				value="currentSecondLevelCacheRegionName">
-				<t:beandisplay object="secondLevelCacheStatistics" />
-			</t:loop>
-
-            <t:parameter name="else">
-                <p>
-                    <em>${message:hibernate-statistics-hidden}</em>
-                </p>
-            </t:parameter>
-		</t:unless>
-	</body>
-</html>
\ No newline at end of file