You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2012/12/05 00:41:08 UTC

[11/52] [partial] ISIS-188: consolidating isis-core

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/InstallerAbstract.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/InstallerAbstract.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/InstallerAbstract.java
new file mode 100644
index 0000000..a8558c3
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/InstallerAbstract.java
@@ -0,0 +1,178 @@
+/*
+ *  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.isis.core.commons.config;
+
+import java.util.Collections;
+import java.util.List;
+
+import com.google.common.collect.Lists;
+
+import org.apache.isis.core.commons.components.Installer;
+
+public abstract class InstallerAbstract implements Installer, IsisConfigurationBuilderAware, IsisConfigurationAware {
+
+    private final String type;
+    private final String name;
+
+    private IsisConfigurationBuilder isisConfigurationBuilder;
+    private IsisConfiguration configuration;
+
+    /**
+     * Subclasses should pass in the type defined as a constant in the
+     * subinterface of Installer.
+     * 
+     * <p>
+     * For example, <tt>PersistenceMechanismInstaller</tt> has a constant
+     * <tt>PersistenceMechanismInstaller#TYPE</tt>. Any implementation of
+     * <tt>PersistenceMechanismInstaller</tt> should pass this constant value up
+     * to this constructor.
+     */
+    public InstallerAbstract(final String type, final String name) {
+        this.type = type;
+        this.name = name;
+    }
+
+    @Override
+    public String getType() {
+        return type;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Returns <tt>[type.properties, type_name.properties</tt>.
+     * 
+     * <p>
+     * For example,
+     * <tt>[persistor.properties, persistor_in-memory.properties]</tt>.
+     * 
+     * @see #getType()
+     * @see #getName()
+     */
+    @Override
+    public List<String> getConfigurationResources() {
+        final List<String> resourceList = Lists.newArrayList();
+        final String componentFile = getType() + ".properties";
+        resourceList.add(componentFile);
+        final String componentImplementationFile = getType() + "_" + getName() + ".properties";
+        resourceList.add(componentImplementationFile);
+        addConfigurationResources(resourceList);
+        return Collections.unmodifiableList(resourceList);
+    }
+
+    /**
+     * Optional hook method to allow subclasses to specify any additional config
+     * resources.
+     */
+    protected void addConfigurationResources(final List<String> configurationResources) {
+    }
+
+    // ////////////////////////////////////////////////////
+    // init, shutdown
+    // ////////////////////////////////////////////////////
+
+    /**
+     * Default implementation does nothing.
+     */
+    @Override
+    public void init() {
+        // no-op implementation, subclasses may override!
+    }
+
+    /**
+     * Default implementation does nothing.
+     */
+    @Override
+    public void shutdown() {
+        // no-op implementation, subclasses may override!
+    }
+
+    /**
+     * Either this method or {@link #setConfiguration(IsisConfiguration)} should
+     * be called prior to calling {@link #getConfiguration()}.
+     * 
+     * <p>
+     * If a {@link #setConfiguration(IsisConfiguration) configuration} has
+     * already been provided, then throws {@link IllegalStateException}.
+     */
+    @Override
+    public void setConfigurationBuilder(final IsisConfigurationBuilder isisConfigurationBuilder) {
+        if (configuration != null) {
+            throw new IllegalStateException("A IsisConfiguration has already been provided.");
+        }
+        this.isisConfigurationBuilder = isisConfigurationBuilder;
+    }
+
+    /**
+     * Either this method or
+     * {@link #setConfigurationBuilder(IsisConfigurationBuilder)} should be
+     * called prior to calling {@link #getConfiguration()}.
+     * 
+     * <p>
+     * If a {@link #setConfigurationBuilder(IsisConfigurationBuilder)
+     * configuration builder} has already been provided, then throws
+     * {@link IllegalStateException}.
+     */
+    public void setConfiguration(final IsisConfiguration configuration) {
+        if (isisConfigurationBuilder != null) {
+            throw new IllegalStateException("A IsisConfigurationBuilder has already been provided.");
+        }
+        this.configuration = configuration;
+    }
+
+    /**
+     * Returns a <i>snapshot</i> of the current configuration provided by the
+     * {@link #setConfigurationBuilder(IsisConfigurationBuilder) injected}
+     * {@link IsisConfigurationBuilder}.
+     * 
+     * <p>
+     * Implementation note: the implementation is in fact just
+     * {@link InstallerLookupDefault}.
+     */
+    public IsisConfiguration getConfiguration() {
+        if (isisConfigurationBuilder != null) {
+            return isisConfigurationBuilder.getConfiguration();
+        } else if (configuration != null) {
+            return configuration;
+        } else {
+            throw new IllegalStateException("Neither a ConfigurationBuilder nor Configuration has not been provided");
+        }
+    }
+
+    /**
+     * Helper for subclasses implementing {@link #getTypes()}.
+     */
+    protected static List<Class<?>> listOf(final Class<?>... classes) {
+        return Collections.unmodifiableList(Lists.<Class<?>> newArrayList(classes));
+    }
+
+    /**
+     * Helper for subclasses implementing {@link #getTypes()}.
+     */
+    protected static List<Class<?>> listOf(final List<Class<?>> classList, final Class<?>... classes) {
+        final List<Class<?>> arrayList = Lists.<Class<?>> newArrayList(classes);
+        arrayList.addAll(0, classList);
+        return Collections.unmodifiableList(arrayList);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfiguration.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfiguration.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfiguration.java
new file mode 100644
index 0000000..f2581f0
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfiguration.java
@@ -0,0 +1,192 @@
+/*
+ *  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.isis.core.commons.config;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.isis.core.commons.components.Injectable;
+import org.apache.isis.core.commons.debug.DebuggableWithTitle;
+import org.apache.isis.core.commons.resource.ResourceStreamSource;
+
+/**
+ * Immutable set of properties representing the configuration of the running
+ * system.
+ * 
+ * <p>
+ * The {@link IsisConfiguration} is one part of a mutable/immutable pair pattern
+ * (cf {@link String} and {@link StringBuilder}). What this means is, as
+ * components are loaded they can discover their own configuration resources.
+ * These are added to {@link IsisConfigurationBuilder}.
+ * 
+ * <p>
+ * Thus the {@link IsisConfiguration} held by different components may vary, but
+ * with each being a possible superset of the previous.
+ */
+public interface IsisConfiguration extends DebuggableWithTitle, Injectable, Iterable<String> {
+
+    /**
+     * Creates a new IsisConfiguration containing the properties starting with
+     * the specified prefix. The names of the new properties will have the
+     * prefixed stripped. This is similar to the {@link #getProperties(String)}
+     * method, except the property names have their prefixes removed.
+     * 
+     * @see #getProperties(String)
+     */
+    IsisConfiguration createSubset(String prefix);
+
+    /**
+     * Gets the boolean value for the specified name where no value or 'on' will
+     * result in true being returned; anything gives false. If no boolean
+     * property is specified with this name then false is returned.
+     * 
+     * @param name
+     *            the property name
+     */
+    boolean getBoolean(String name);
+
+    /**
+     * Gets the boolean value for the specified name. If no property is
+     * specified with this name then the specified default boolean value is
+     * returned.
+     * 
+     * @param name
+     *            the property name
+     * @param defaultValue
+     *            the value to use as a default
+     */
+    boolean getBoolean(String name, boolean defaultValue);
+
+    /**
+     * Gets the color for the specified name. If no color property is specified
+     * with this name then null is returned.
+     * 
+     * @param name
+     *            the property name
+     */
+    Color getColor(String name);
+
+    /**
+     * Gets the color for the specified name. If no color property is specified
+     * with this name then the specified default color is returned.
+     * 
+     * @param name
+     *            the property name
+     * @param defaultValue
+     *            the value to use as a default
+     */
+    Color getColor(String name, Color defaultValue);
+
+    /**
+     * Gets the font for the specified name. If no font property is specified
+     * with this name then null is returned.
+     * 
+     * @param name
+     *            the property name
+     */
+    Font getFont(String name);
+
+    /**
+     * Gets the font for the specified name. If no font property is specified
+     * with this name then the specified default font is returned.
+     * 
+     * @param name
+     *            the property name
+     * @param defaultValue
+     *            the color to use as a default
+     */
+    Font getFont(String name, Font defaultValue);
+
+    /**
+     * Returns a list of entries for the single configuration property with the
+     * specified name.
+     * 
+     * <p>
+     * If there is no matching property then returns an empty array.
+     */
+    String[] getList(String name);
+
+    /**
+     * Gets the number value for the specified name. If no property is specified
+     * with this name then 0 is returned.
+     * 
+     * @param name
+     *            the property name
+     */
+    int getInteger(String name);
+
+    /**
+     * Gets the number value for the specified name. If no property is specified
+     * with this name then the specified default number value is returned.
+     * 
+     * @param name
+     *            the property name
+     * @param defaultValue
+     *            the value to use as a default
+     */
+    int getInteger(String name, int defaultValue);
+
+    /**
+     * Creates a new IsisConfiguration containing the properties starting with
+     * the specified prefix. The names of the properties in the copy are the
+     * same as in the original, ie the prefix is not removed. This is similar to
+     * the {@link #createSubset(String)} method except the names of the
+     * properties are not altered when copied.
+     * 
+     * @see #createSubset(String)
+     */
+    IsisConfiguration getProperties(String withPrefix);
+
+    /**
+     * Returns the configuration property with the specified name. If there is
+     * no matching property then null is returned.
+     */
+    String getString(String name);
+
+    String getString(String name, String defaultValue);
+
+    boolean hasProperty(String name);
+
+    boolean isEmpty();
+
+    /**
+     * Iterates over the property names of this configuration.
+     */
+    @Override
+    Iterator<String> iterator();
+
+    int size();
+
+    /**
+     * The {@link ResourceStreamSource} that was used to build this
+     * configuration.
+     * 
+     * @see IsisConfigurationBuilder#getResourceStreamSource()
+     */
+    ResourceStreamSource getResourceStreamSource();
+
+    /**
+     * A mutable copy of the current set of properties (name/values) held in this configuration.
+     */
+    Map<String, String> asMap();
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationAware.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationAware.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationAware.java
new file mode 100644
index 0000000..09bc9f3
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationAware.java
@@ -0,0 +1,32 @@
+/*
+ *  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.isis.core.commons.config;
+
+/**
+ * Allows components (eg facet factories) to indicate that they have a
+ * dependency on {@link IsisConfiguration}.
+ */
+public interface IsisConfigurationAware {
+
+    /**
+     * Inject the {@link IsisConfiguration} into the component.
+     */
+    void setConfiguration(IsisConfiguration configuration);
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationBuilder.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationBuilder.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationBuilder.java
new file mode 100644
index 0000000..ddfa5ea
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationBuilder.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.isis.core.commons.config;
+
+import java.util.Properties;
+
+import org.apache.isis.core.commons.components.Injectable;
+import org.apache.isis.core.commons.resource.ResourceStreamSource;
+import org.apache.isis.core.commons.resource.ResourceStreamSourceChainOfResponsibility;
+
+/**
+ * Holds a mutable set of properties representing the configuration.
+ * 
+ * <p>
+ * Mutable/immutable pair with the {@link IsisConfiguration}. To obtain the
+ * configuration, use {@link #getConfiguration()}.
+ * 
+ * @see IsisConfiguration for more details on the mutable/immutable pair
+ *      pattern.
+ */
+public interface IsisConfigurationBuilder extends Injectable {
+
+    /**
+     * Returns a currently known {@link IsisConfiguration}.
+     */
+    IsisConfiguration getConfiguration();
+
+    public abstract void addConfigurationResource(final String installerName, final NotFoundPolicy notFoundPolicy);
+
+    public abstract void add(final Properties properties);
+
+    public abstract void add(final String key, final String value);
+
+    /**
+     * The underlying {@link ResourceStreamSource} from which the configuration
+     * is being read.
+     * 
+     * <p>
+     * Note that this may be a {@link ResourceStreamSourceChainOfResponsibility composite}.
+     */
+    public ResourceStreamSource getResourceStreamSource();
+    
+    /**
+     * Log a summary of resources found or not found.
+     */
+    public void dumpResourcesToLog();
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationBuilderAware.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationBuilderAware.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationBuilderAware.java
new file mode 100644
index 0000000..07023b7
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationBuilderAware.java
@@ -0,0 +1,25 @@
+/*
+ *  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.isis.core.commons.config;
+
+public interface IsisConfigurationBuilderAware {
+
+    void setConfigurationBuilder(IsisConfigurationBuilder configurationLoader);
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationBuilderDefault.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationBuilderDefault.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationBuilderDefault.java
new file mode 100644
index 0000000..ffa710e
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationBuilderDefault.java
@@ -0,0 +1,75 @@
+/*
+ *  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.isis.core.commons.config;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import com.google.common.collect.Lists;
+
+import org.apache.log4j.Logger;
+
+import org.apache.isis.core.commons.resource.ResourceStreamSource;
+import org.apache.isis.core.commons.resource.ResourceStreamSourceContextLoaderClassPath;
+import org.apache.isis.core.commons.resource.ResourceStreamSourceFileSystem;
+
+/**
+ * Convenience implementation of {@link IsisConfigurationBuilder} that loads
+ * configuration resource as per {@link IsisConfigurationBuilderFileSystem} and
+ * otherwise from the {@link ResourceStreamSourceContextLoaderClassPath
+ * classpath}.
+ * 
+ * @see ResourceStreamSourceFileSystem
+ */
+public class IsisConfigurationBuilderDefault extends IsisConfigurationBuilderResourceStreams {
+
+    @SuppressWarnings("unused")
+    private static final Logger LOG = Logger.getLogger(IsisConfigurationBuilderDefault.class);
+
+    public IsisConfigurationBuilderDefault() {
+        super(resourceStreamSources(null));
+    }
+
+    public IsisConfigurationBuilderDefault(final String firstDirectory) {
+        super(resourceStreamSources(firstDirectory));
+    }
+
+    private static ResourceStreamSource[] resourceStreamSources(final String firstDirectory) {
+        final ArrayList<ResourceStreamSource> rssList = Lists.newArrayList();
+        if (firstDirectory != null) {
+            rssList.add(fromFileSystem(firstDirectory));
+        }
+        rssList.addAll(Arrays.asList(fromFileSystem(ConfigurationConstants.DEFAULT_CONFIG_DIRECTORY), fromFileSystem(ConfigurationConstants.WEBINF_FULL_DIRECTORY), fromClassPath(), fromClassPath(ConfigurationConstants.WEBINF_DIRECTORY)));
+        return rssList.toArray(new ResourceStreamSource[0]);
+    }
+
+    private static ResourceStreamSource fromFileSystem(final String directory) {
+        return ResourceStreamSourceFileSystem.create(directory);
+    }
+
+    private static ResourceStreamSource fromClassPath() {
+        return ResourceStreamSourceContextLoaderClassPath.create();
+    }
+
+    private static ResourceStreamSource fromClassPath(final String prefix) {
+        return ResourceStreamSourceContextLoaderClassPath.create(prefix);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationBuilderFileSystem.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationBuilderFileSystem.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationBuilderFileSystem.java
new file mode 100644
index 0000000..825b25f
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationBuilderFileSystem.java
@@ -0,0 +1,56 @@
+/*
+ *  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.isis.core.commons.config;
+
+import org.apache.log4j.Logger;
+
+import org.apache.isis.core.commons.resource.ResourceStreamSource;
+import org.apache.isis.core.commons.resource.ResourceStreamSourceChainOfResponsibility;
+import org.apache.isis.core.commons.resource.ResourceStreamSourceFileSystem;
+
+/**
+ * Convenience implementation of {@link IsisConfigurationBuilder} that loads
+ * configuration resource from a specified directory (or directories) on the
+ * filesystem.
+ * 
+ * @see ResourceStreamSourceFileSystem
+ */
+public class IsisConfigurationBuilderFileSystem extends IsisConfigurationBuilderResourceStreams {
+
+    @SuppressWarnings("unused")
+    private static final Logger LOG = Logger.getLogger(IsisConfigurationBuilderFileSystem.class);
+
+    private static ResourceStreamSource createResourceStreamSource(final String... directories) {
+        final ResourceStreamSourceChainOfResponsibility composite = new ResourceStreamSourceChainOfResponsibility();
+        for (final String directory : directories) {
+            composite.addResourceStreamSource(new ResourceStreamSourceFileSystem(directory));
+        }
+        return composite;
+    }
+
+    public IsisConfigurationBuilderFileSystem(final String... directories) {
+        super(createResourceStreamSource(directories));
+    }
+
+    public IsisConfigurationBuilderFileSystem() {
+        super(ResourceStreamSourceFileSystem.create(ConfigurationConstants.DEFAULT_CONFIG_DIRECTORY));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationBuilderPrimer.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationBuilderPrimer.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationBuilderPrimer.java
new file mode 100644
index 0000000..70e4df8
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationBuilderPrimer.java
@@ -0,0 +1,26 @@
+/*
+ *  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.isis.core.commons.config;
+
+public interface IsisConfigurationBuilderPrimer {
+
+    void primeConfigurationBuilder(IsisConfigurationBuilder isisConfigurationBuilder);
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationBuilderResourceStreams.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationBuilderResourceStreams.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationBuilderResourceStreams.java
new file mode 100644
index 0000000..bdab1a1
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationBuilderResourceStreams.java
@@ -0,0 +1,328 @@
+/*
+ *  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.isis.core.commons.config;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.Sets;
+
+import org.apache.log4j.Logger;
+
+import org.apache.isis.core.commons.exceptions.IsisException;
+import org.apache.isis.core.commons.resource.ResourceStreamSource;
+import org.apache.isis.core.commons.resource.ResourceStreamSourceChainOfResponsibility;
+import org.apache.isis.core.commons.resource.ResourceStreamSourceFileSystem;
+
+/**
+ * Adapter for {@link IsisConfigurationBuilder}, loading the specified
+ * configuration resource (file) from the given {@link ResourceStreamSource}(s).
+ * 
+ * <p>
+ * If a property is in multiple configuration resources then the latter
+ * resources will overwrite the former.
+ */
+public class IsisConfigurationBuilderResourceStreams implements IsisConfigurationBuilder {
+
+    private static final Logger LOG = Logger.getLogger(IsisConfigurationBuilderResourceStreams.class);
+
+    private final Set<String> configurationResourcesFound = Sets.newLinkedHashSet();
+    private final Set<String> configurationResourcesNotFound = Sets.newLinkedHashSet();
+    
+    static class ConfigurationResourceAndPolicy {
+        private final String configurationResource;
+        private final NotFoundPolicy notFoundPolicy;
+
+        public ConfigurationResourceAndPolicy(final String configurationResource, final NotFoundPolicy notFoundPolicy) {
+            this.configurationResource = configurationResource;
+            this.notFoundPolicy = notFoundPolicy;
+        }
+
+        public String getConfigurationResource() {
+            return configurationResource;
+        }
+
+        public NotFoundPolicy getNotFoundPolicy() {
+            return notFoundPolicy;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("%s{%s}", configurationResource, notFoundPolicy);
+        }
+    }
+
+    private final ResourceStreamSource resourceStreamSource;
+
+    private final List<ConfigurationResourceAndPolicy> configurationResources = new ArrayList<ConfigurationResourceAndPolicy>();
+    private final Properties additionalProperties = new Properties();
+    private boolean includeSystemProperties = false;
+
+    /**
+     * Most recent snapshot of {@link IsisConfiguration} obtained from
+     * {@link #configurationLoader}.
+     * 
+     * <p>
+     * Whenever further configuration is merged in, this cache is invalidated.
+     */
+    private IsisConfiguration cachedConfiguration;
+
+    // ////////////////////////////////////////////////////////////
+    // Constructor, initialization
+    // ////////////////////////////////////////////////////////////
+
+    private static ResourceStreamSource createComposite(final ResourceStreamSource... resourceStreamSources) {
+        final ResourceStreamSourceChainOfResponsibility composite = new ResourceStreamSourceChainOfResponsibility();
+        for (final ResourceStreamSource rss : resourceStreamSources) {
+            if (rss == null) {
+                continue;
+            }
+            composite.addResourceStreamSource(rss);
+        }
+        return composite;
+    }
+
+    public IsisConfigurationBuilderResourceStreams() {
+        this(ResourceStreamSourceFileSystem.create(ConfigurationConstants.DEFAULT_CONFIG_DIRECTORY));
+    }
+
+    public IsisConfigurationBuilderResourceStreams(final ResourceStreamSource... resourceStreamSources) {
+        this(createComposite(resourceStreamSources));
+    }
+
+    public IsisConfigurationBuilderResourceStreams(final ResourceStreamSource resourceStreamSource) {
+        this.resourceStreamSource = resourceStreamSource;
+        addDefaultConfigurationResources();
+    }
+
+    /**
+     * May be overridden by subclasses if required.
+     */
+    protected void addDefaultConfigurationResources() {
+        addConfigurationResource(ConfigurationConstants.DEFAULT_CONFIG_FILE, NotFoundPolicy.FAIL_FAST);
+        addConfigurationResource(ConfigurationConstants.WEB_CONFIG_FILE, NotFoundPolicy.CONTINUE);
+    }
+
+    // ////////////////////////////////////////////////////////////
+    // ResourceStreamSource
+    // ////////////////////////////////////////////////////////////
+
+    @Override
+    public ResourceStreamSource getResourceStreamSource() {
+        return resourceStreamSource;
+    }
+
+    // ////////////////////////////////////////////////////////////
+    // populating or updating
+    // ////////////////////////////////////////////////////////////
+
+    /**
+     * Registers the configuration resource (usually, a file) with the specified
+     * name from the first {@link ResourceStreamSource} available.
+     * 
+     * <p>
+     * If the configuration resource cannot be found then the provided
+     * {@link NotFoundPolicy} determines whether an exception is thrown or not.
+     * 
+     * <p>
+     * Must be called before {@link #getConfiguration()}; the resource is
+     * actually read on {@link #getConfiguration()}.
+     */
+    @Override
+    public synchronized void addConfigurationResource(final String configurationResource, final NotFoundPolicy notFoundPolicy) {
+        configurationResources.add(new ConfigurationResourceAndPolicy(configurationResource, notFoundPolicy));
+        invalidateCache();
+    }
+
+    public synchronized void setIncludeSystemProperties(final boolean includeSystemProperties) {
+        this.includeSystemProperties = includeSystemProperties;
+        invalidateCache();
+    }
+
+    /**
+     * Adds additional property.
+     */
+    @Override
+    public synchronized void add(final String key, final String value) {
+        if (key == null || value == null) {
+            return;
+        }
+        additionalProperties.setProperty(key, value);
+        if (LOG.isInfoEnabled()) {
+            LOG.info("added " + key + "=" + value);
+        }
+        invalidateCache();
+    }
+
+    /**
+     * Adds additional properties.
+     */
+    @Override
+    public synchronized void add(final Properties properties) {
+        final Enumeration<?> keys = properties.propertyNames();
+        while (keys.hasMoreElements()) {
+            final String key = (String) keys.nextElement();
+            add(key, properties.getProperty(key));
+        }
+        invalidateCache();
+    }
+
+    // ////////////////////////////////////////////////////////////
+    // getConfiguration
+    // ////////////////////////////////////////////////////////////
+
+    /**
+     * Returns the current {@link IsisConfiguration configuration}.
+     */
+    @Override
+    public synchronized IsisConfiguration getConfiguration() {
+        if (cachedConfiguration != null) {
+            return cachedConfiguration;
+        }
+
+        final IsisConfigurationDefault configuration = new IsisConfigurationDefault(getResourceStreamSource());
+        loadConfigurationResources(configuration);
+        // TODO: this hack should move elsewhere, where the DeploymentType is
+        // known.
+        addShowExplorationOptionsIfNotSpecified(configuration);
+        addSystemPropertiesIfRequested(configuration);
+        addAdditionalProperties(configuration);
+        return cachedConfiguration = configuration;
+    }
+
+    private void loadConfigurationResources(final IsisConfigurationDefault configuration) {
+        for (final ConfigurationResourceAndPolicy configResourceAndPolicy : configurationResources) {
+            loadConfigurationResource(configuration, configResourceAndPolicy);
+        }
+    }
+
+    private void loadConfigurationResource(final IsisConfigurationDefault configuration, final ConfigurationResourceAndPolicy configResourceAndPolicy) {
+        final String configurationResource = configResourceAndPolicy.getConfigurationResource();
+        final NotFoundPolicy notFoundPolicy = configResourceAndPolicy.getNotFoundPolicy();
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("loading configuration resource: " + configurationResource + ", notFoundPolicy: " + notFoundPolicy);
+        }
+        loadConfigurationResource(configuration, configurationResource, notFoundPolicy);
+    }
+
+    /**
+     * Loads the configuration resource (usually, a file) with the specified
+     * name from the first {@link ResourceStreamSource} available.
+     * 
+     * <p>
+     * If the configuration resource cannot be found then the provided
+     * {@link NotFoundPolicy} determines whether an exception is thrown or not.
+     */
+    protected void loadConfigurationResource(final IsisConfigurationDefault configuration, final String configurationResource, final NotFoundPolicy notFoundPolicy) {
+        try {
+            final PropertiesReader propertiesReader = loadConfigurationResource(resourceStreamSource, configurationResource);
+            addProperties(configuration, propertiesReader.getProperties());
+            configurationResourcesFound.add(configurationResource);
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("'" + configurationResource + "' FOUND");
+            }
+            return;
+        } catch (final IOException ex) {
+            // keep going
+        }
+        if (notFoundPolicy == NotFoundPolicy.FAIL_FAST) {
+            throw new IsisException("failed to load '" + configurationResource + "'; tried using: " + resourceStreamSource.getName());
+        } else {
+            configurationResourcesNotFound.add(configurationResource);
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("'" + configurationResource + "' not found, but not needed");
+            }
+        }
+    }
+
+    private PropertiesReader loadConfigurationResource(final ResourceStreamSource resourceStreamSource, final String configurationResource) throws IOException {
+        return new PropertiesReader(resourceStreamSource, configurationResource);
+    }
+
+    private void addShowExplorationOptionsIfNotSpecified(final IsisConfigurationDefault configuration) {
+        if (configuration.getString(ConfigurationConstants.SHOW_EXPLORATION_OPTIONS) == null) {
+            configuration.add(ConfigurationConstants.SHOW_EXPLORATION_OPTIONS, "yes");
+        }
+    }
+
+    private void addSystemPropertiesIfRequested(final IsisConfigurationDefault configuration) {
+        if (includeSystemProperties) {
+            addProperties(configuration, System.getProperties());
+        }
+    }
+
+    private void addAdditionalProperties(final IsisConfigurationDefault configuration) {
+        addProperties(configuration, additionalProperties);
+    }
+
+    protected void addProperties(final IsisConfigurationDefault configuration, final Properties properties) {
+        configuration.add(properties);
+    }
+
+    private void invalidateCache() {
+        cachedConfiguration = null;
+    }
+
+
+    
+    // ////////////////////////////////////////////////////////////
+    // Logging
+    // ////////////////////////////////////////////////////////////
+
+    @Override
+    public void dumpResourcesToLog() {
+        if (LOG.isInfoEnabled()) {
+            LOG.info("Configuration resources FOUND:");
+            for (String resource : configurationResourcesFound) {
+                LOG.info("*  " + resource);
+            }
+        }
+        if (LOG.isInfoEnabled()) {
+            LOG.info("Configuration resources NOT FOUND (but not needed):");
+            for (String resource : configurationResourcesNotFound) {
+                LOG.info("*  " + resource);
+            }
+        }
+    }
+
+
+    // ////////////////////////////////////////////////////////////
+    // Injectable
+    // ////////////////////////////////////////////////////////////
+
+    @Override
+    public void injectInto(final Object candidate) {
+        if (IsisConfigurationBuilderAware.class.isAssignableFrom(candidate.getClass())) {
+            final IsisConfigurationBuilderAware cast = IsisConfigurationBuilderAware.class.cast(candidate);
+            cast.setConfigurationBuilder(this);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(this).add("resourceStream", resourceStreamSource).add("configResources", configurationResources).toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationDefault.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationDefault.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationDefault.java
new file mode 100644
index 0000000..351efba
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationDefault.java
@@ -0,0 +1,397 @@
+/*
+ *  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.isis.core.commons.config;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+import com.google.common.collect.Maps;
+
+import org.apache.log4j.Logger;
+
+import org.apache.isis.core.commons.debug.DebugBuilder;
+import org.apache.isis.core.commons.exceptions.IsisException;
+import org.apache.isis.core.commons.resource.ResourceStreamSource;
+
+public class IsisConfigurationDefault implements IsisConfiguration {
+    
+    private static final Logger LOG = Logger.getLogger(IsisConfigurationDefault.class);
+    private final Properties properties = new Properties();
+    private final ResourceStreamSource resourceStreamSource;
+
+    // ////////////////////////////////////////////////
+    // Constructor
+    // ////////////////////////////////////////////////
+
+    public IsisConfigurationDefault() {
+        this(null);
+    }
+
+    public IsisConfigurationDefault(final ResourceStreamSource resourceStreamSource) {
+        this.resourceStreamSource = resourceStreamSource;
+        LOG.debug("from :" + nameOf(resourceStreamSource));
+    }
+
+    private String nameOf(final ResourceStreamSource resourceStreamSource) {
+        return resourceStreamSource != null ? resourceStreamSource.getName() : null;
+    }
+
+    // ////////////////////////////////////////////////
+    // ResourceStreamSource
+    // ////////////////////////////////////////////////
+
+    @Override
+    public ResourceStreamSource getResourceStreamSource() {
+        return resourceStreamSource;
+    }
+
+    // ////////////////////////////////////////////////
+    // add
+    // ////////////////////////////////////////////////
+
+    /**
+     * Add the properties from an existing Properties object.
+     */
+    public void add(final Properties properties) {
+        for(Object key: properties.keySet()) {
+        	Object value = properties.get(key);
+        	add((String)key, (String)value);
+        }
+    }
+
+    /**
+     * Adds a key-value pair to this set of properties
+     */
+    public void add(final String key, final String value) {
+        if (key == null) {
+            return;
+        }
+    	if (properties.containsKey(key)) {
+    		LOG.info("replacing " + key + "=" + properties.get(key) + " with " + value);
+    	}
+    	properties.put(key, value);
+    }
+
+    @Override
+    public IsisConfiguration createSubset(final String prefix) {
+        final IsisConfigurationDefault subset = new IsisConfigurationDefault(resourceStreamSource);
+
+        String startsWith = prefix;
+        if (!startsWith.endsWith(".")) {
+            startsWith = startsWith + '.';
+        }
+        final int prefixLength = startsWith.length();
+
+        for(Object keyObj: properties.keySet()) {
+            final String key = (String)keyObj;
+            if (key.startsWith(startsWith)) {
+                final String modifiedKey = key.substring(prefixLength);
+                subset.properties.put(modifiedKey, properties.get(key));
+            }
+        }
+        return subset;
+    }
+
+    // ////////////////////////////////////////////////
+    // getXxx
+    // ////////////////////////////////////////////////
+
+    /**
+     * Gets the boolean value for the specified name where no value or 'on' will
+     * result in true being returned; anything gives false. If no boolean
+     * property is specified with this name then false is returned.
+     * 
+     * @param name
+     *            the property name
+     */
+    @Override
+    public boolean getBoolean(final String name) {
+        return getBoolean(name, false);
+    }
+
+    /**
+     * Gets the boolean value for the specified name. If no property is
+     * specified with this name then the specified default boolean value is
+     * returned.
+     * 
+     * @param name
+     *            the property name
+     * @param defaultValue
+     *            the value to use as a default
+     */
+    @Override
+    public boolean getBoolean(final String name, final boolean defaultValue) {
+        String value = getProperty(name);
+        if (value == null) {
+            return defaultValue;
+        }
+        value = value.toLowerCase();
+        if (value.equals("on") || value.equals("yes") || value.equals("true") || value.equals("")) {
+            return true;
+        }
+        if (value.equals("off") || value.equals("no") || value.equals("false")) {
+            return false;
+        }
+
+        throw new IsisConfigurationException("Illegal flag for " + name + "; must be one of on, off, yes, no, true or false");
+    }
+
+    /**
+     * Gets the color for the specified name. If no color property is specified
+     * with this name then null is returned.
+     * 
+     * @param name
+     *            the property name
+     */
+    @Override
+    public Color getColor(final String name) {
+        return getColor(name, null);
+    }
+
+    /**
+     * Gets the color for the specified name. If no color property is specified
+     * with this name then the specified default color is returned.
+     * 
+     * @param name
+     *            the property name
+     * @param defaultValue
+     *            the value to use as a default
+     */
+    @Override
+    public Color getColor(final String name, final Color defaultValue) {
+        final String color = getProperty(name);
+
+        if (color == null) {
+            return defaultValue;
+        }
+
+        return Color.decode(color);
+    }
+
+    @Override
+    public void debugData(final DebugBuilder str) {
+        str.appendln("Resource Stream Source", resourceStreamSource);
+        str.appendln();
+        final Enumeration<?> names = properties.propertyNames();
+        while (names.hasMoreElements()) {
+            final String name = (String) names.nextElement();
+            str.append(name, 55);
+            str.append(" = ");
+            str.appendln(properties.getProperty(name));
+        }
+    }
+
+    @Override
+    public String debugTitle() {
+        return "Properties Configuration";
+    }
+
+    /**
+     * Gets the font for the specified name. If no font property is specified
+     * with this name then null is returned.
+     * 
+     * @param name
+     *            the property name
+     */
+    @Override
+    public Font getFont(final String name) {
+        return getFont(name, null);
+    }
+
+    /**
+     * Gets the font for the specified name. If no font property is specified
+     * with this name then the specified default font is returned.
+     * 
+     * @param name
+     *            the property name
+     * @param defaultValue
+     *            the color to use as a default
+     */
+    @Override
+    public Font getFont(final String name, final Font defaultValue) {
+        final String font = getProperty(name);
+
+        if (font == null) {
+            return defaultValue;
+        }
+
+        return Font.decode(font);
+    }
+
+    /**
+     * Gets the number value for the specified name. If no property is specified
+     * with this name then 0 is returned.
+     * 
+     * @param name
+     *            the property name
+     */
+    @Override
+    public int getInteger(final String name) {
+        return getInteger(name, 0);
+    }
+
+    /**
+     * Gets the number value for the specified name. If no property is specified
+     * with this name then the specified default number value is returned.
+     * 
+     * @param name
+     *            the property name
+     * @param defaultValue
+     *            the value to use as a default
+     */
+    @Override
+    public int getInteger(final String name, final int defaultValue) {
+        final String value = getProperty(name);
+
+        if (value == null) {
+            return defaultValue;
+        }
+
+        return Integer.valueOf(value).intValue();
+    }
+
+    @Override
+    public String[] getList(final String name) {
+        final String list = getString(name);
+        if (list == null) {
+            return new String[0];
+        } else {
+            final StringTokenizer tokens = new StringTokenizer(list, ConfigurationConstants.LIST_SEPARATOR);
+            final String array[] = new String[tokens.countTokens()];
+            int i = 0;
+            while (tokens.hasMoreTokens()) {
+                array[i++] = tokens.nextToken().trim();
+            }
+            return array;
+        }
+    }
+
+    @Override
+    public IsisConfiguration getProperties(final String withPrefix) {
+        final int prefixLength = "".length();
+
+        final Properties pp = new Properties();
+        final Enumeration<?> e = properties.keys();
+        while (e.hasMoreElements()) {
+            final String key = (String) e.nextElement();
+            if (key.startsWith(withPrefix)) {
+                final String modifiedKey = key.substring(prefixLength);
+                pp.put(modifiedKey, properties.get(key));
+            }
+        }
+        final IsisConfigurationDefault isisConfigurationDefault = new IsisConfigurationDefault(resourceStreamSource);
+        isisConfigurationDefault.add(pp);
+        return isisConfigurationDefault;
+    }
+
+    private String getProperty(final String name) {
+        return getProperty(name, null);
+    }
+
+    private String getProperty(final String name, final String defaultValue) {
+        final String key = referedToAs(name);
+        if (key.indexOf("..") >= 0) {
+            throw new IsisException("property names should not have '..' within them: " + name);
+        }
+        String property = properties.getProperty(key, defaultValue);
+        property = property != null ? property.trim() : null;
+        LOG.debug("property: '" + key + "' =  '" + property + "'");
+        return property;
+    }
+
+    /**
+     * Returns the configuration property with the specified name. If there is
+     * no matching property then null is returned.
+     */
+    @Override
+    public String getString(final String name) {
+        return getProperty(name);
+    }
+
+    @Override
+    public String getString(final String name, final String defaultValue) {
+        return getProperty(name, defaultValue);
+    }
+
+    @Override
+    public boolean hasProperty(final String name) {
+        final String key = referedToAs(name);
+        return properties.containsKey(key);
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return properties.isEmpty();
+    }
+
+    @Override
+    public Iterator<String> iterator() {
+        return properties.stringPropertyNames().iterator();
+    }
+
+    /**
+     * Returns as a String that the named property is refered to as. For example
+     * in a simple properties file the property z might be specified in the file
+     * as x.y.z.
+     */
+    private String referedToAs(final String name) {
+        return name;
+    }
+
+    @Override
+    public int size() {
+        return properties.size();
+    }
+
+    @Override
+    public String toString() {
+        return "ConfigurationParameters [properties=" + properties + "]";
+    }
+
+    // ////////////////////////////////////////////////////////////////////
+    // injectInto
+    // ////////////////////////////////////////////////////////////////////
+
+    @Override
+    public void injectInto(final Object candidate) {
+        if (IsisConfigurationAware.class.isAssignableFrom(candidate.getClass())) {
+            final IsisConfigurationAware cast = IsisConfigurationAware.class.cast(candidate);
+            cast.setConfiguration(this);
+        }
+    }
+
+    @Override
+    public Map<String,String> asMap() {
+        final Map<String, String> map = Maps.newHashMap();
+        for(String propertyName: this) {
+            final String propertyValue = this.getProperty(propertyName);
+            map.put(propertyName, propertyValue);
+        }
+        return map;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationException.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationException.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationException.java
new file mode 100644
index 0000000..706d969
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/IsisConfigurationException.java
@@ -0,0 +1,42 @@
+/*
+ *  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.isis.core.commons.config;
+
+import org.apache.isis.core.commons.exceptions.IsisException;
+
+public class IsisConfigurationException extends IsisException {
+    private static final long serialVersionUID = 1L;
+
+    public IsisConfigurationException() {
+        super();
+    }
+
+    public IsisConfigurationException(final String s) {
+        super(s);
+    }
+
+    public IsisConfigurationException(final String msg, final Throwable cause) {
+        super(msg, cause);
+    }
+
+    public IsisConfigurationException(final Throwable cause) {
+        super(cause);
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/JmxBeanServer.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/JmxBeanServer.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/JmxBeanServer.java
new file mode 100644
index 0000000..c336267
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/JmxBeanServer.java
@@ -0,0 +1,72 @@
+/*
+ *  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.isis.core.commons.config;
+
+import java.lang.management.ManagementFactory;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.NotCompliantMBeanException;
+import javax.management.ObjectName;
+
+import org.apache.log4j.Logger;
+
+public class JmxBeanServer {
+
+    private static final Logger LOG = Logger.getLogger(JmxBeanServer.class);
+
+    private static JmxBeanServer instance;
+    private final MBeanServer server;
+
+    private JmxBeanServer() {
+        server = ManagementFactory.getPlatformMBeanServer();
+        instance = this;
+    }
+
+    public static JmxBeanServer getInstance() {
+        if (instance == null) {
+            LOG.info("JMX bean server created");
+            instance = new JmxBeanServer();
+        }
+        return instance;
+    }
+
+    public void register(final String name, final Object object) {
+        try {
+            final ObjectName objectName = new ObjectName("Isis:name=" + name);
+            server.registerMBean(object, objectName);
+            LOG.info(name + " JMX mbean registered: " + object);
+        } catch (final MalformedObjectNameException e) {
+            throw new RuntimeException(e);
+        } catch (final NullPointerException e) {
+            throw new RuntimeException(e);
+        } catch (final InstanceAlreadyExistsException e) {
+            LOG.info(name + " JMX mbean already registered: " + object);
+        } catch (final MBeanRegistrationException e) {
+            throw new RuntimeException(e);
+        } catch (final NotCompliantMBeanException e) {
+            throw new RuntimeException(e);
+        }
+
+    }
+}
+
+// Copyright (c) Naked Objects Group Ltd.

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/NotFoundPolicy.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/NotFoundPolicy.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/NotFoundPolicy.java
new file mode 100644
index 0000000..0ab7b81
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/NotFoundPolicy.java
@@ -0,0 +1,24 @@
+/*
+ *  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.isis.core.commons.config;
+
+public enum NotFoundPolicy {
+    CONTINUE, FAIL_FAST
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/PropertiesReader.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/PropertiesReader.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/PropertiesReader.java
new file mode 100644
index 0000000..fa8465e
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/PropertiesReader.java
@@ -0,0 +1,54 @@
+/*
+ *  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.isis.core.commons.config;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+import org.apache.isis.core.commons.lang.IoUtils;
+import org.apache.isis.core.commons.resource.ResourceStreamSource;
+
+/**
+ * Loads properties using the specified {@link ResourceStreamSource}.
+ */
+class PropertiesReader {
+
+    private final Properties properties = new Properties();
+
+    public PropertiesReader(final ResourceStreamSource resourceStream, final String configurationResource) throws IOException {
+
+        InputStream in = null;
+        try {
+            in = resourceStream.readResource(configurationResource);
+            if (in == null) {
+                throw new IOException("Unable to find resource " + configurationResource);
+            }
+            properties.load(in);
+        } finally {
+            IoUtils.closeSafely(in);
+        }
+    }
+
+    public Properties getProperties() {
+        return properties;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/package-info.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/package-info.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/package-info.java
new file mode 100644
index 0000000..db80586
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/config/package-info.java
@@ -0,0 +1,40 @@
+/*
+ *  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.
+ */
+
+/**
+ * Defines the {@link org.apache.isis.core.commons.config.IsisConfiguration}
+ * which collects an immutable set of configuration options (like a hashmap), 
+ * along with a number of supporting classes.
+ * 
+ * <p>
+ * Chief among these supporting classes is {@link org.apache.isis.core.commons.config.ConfigurationBuilder},
+ * which holds a (mutable) collection of properties and is used to build an 
+ * {@link org.apache.isis.core.commons.config.IsisConfiguration}.  The 
+ * {@link org.apache.isis.core.commons.config.ConfigurationBuilder} and
+ * {@link org.apache.isis.core.commons.config.IsisConfiguration} types form
+ * an mutable/immutable pair (cf {@link java.lang.StringBuilder} / {@link java.lang.String}).
+ * 
+ * <p>
+ * The {@link org.apache.isis.core.commons.config.ConfigurationBuilder} is used
+ * by {@link org.apache.isis.core.commons.config.InstallerAbstract}, an
+ * implementation of {@link org.apache.isis.core.commons.components.Installer} 
+ * that allows the configuration to be added to as each component is
+ * installed. 
+ */
+package org.apache.isis.core.commons.config;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/debug/DebugBuilder.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/debug/DebugBuilder.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/debug/DebugBuilder.java
new file mode 100644
index 0000000..6964483
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/debug/DebugBuilder.java
@@ -0,0 +1,139 @@
+/*
+ *  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.isis.core.commons.debug;
+
+public interface DebugBuilder {
+
+    /**
+     * Concatenate the contents of the specified debug builder to the current
+     * builder.
+     */
+    void concat(DebugBuilder debug);
+
+    /**
+     * Append the specified number within a space (number of spaces) specified
+     * by the width. E.g. "15 " where number is 15 and width is 4.
+     */
+    void append(final int number, final int width);
+
+    /**
+     * Append the specified object by calling it <code>toString()</code> method.
+     */
+    void append(final Object object);
+
+    /**
+     * Append the specified object by calling its <code>toString()</code>
+     * method, placing it within specified space.
+     */
+    void append(final Object object, final int width);
+
+    /**
+     * Append the specified number, displayed in hexadecimal notation, with the
+     * specified label, then start a new line.
+     */
+    void appendAsHexln(final String label, final long value);
+
+    /**
+     * Append the message and trace of the specified exception.
+     */
+    void appendException(final Throwable e);
+
+    /**
+     * Start a new line.
+     * 
+     * @see #blankLine()
+     */
+    void appendln();
+
+    /**
+     * Append the specified text, then start a new line.
+     */
+    void appendln(final String text);
+
+    /**
+     * Append the specified text without any formatting.
+     */
+    void appendPreformatted(final String text);
+
+    /**
+     * Append the specified value, displayed as true or false, with the
+     * specified label, then start a new line.
+     */
+    void appendln(final String label, final boolean value);
+
+    /**
+     * Append the specified number with the specified label, then start a new
+     * line.
+     */
+    void appendln(final String label, final double value);
+
+    /**
+     * Append the specified number, displayed in hexadecimal notation, with the
+     * specified label, then start a new line.
+     */
+    void appendln(final String label, final long value);
+
+    /**
+     * Append the specified preformatted text with the specified label, then
+     * start a new line.
+     */
+    void appendPreformatted(final String label, final String text);
+
+    /**
+     * Append the specified object with the specified label, then start a new
+     * line.
+     */
+    void appendln(final String label, final Object object);
+
+    /**
+     * Append the elements of the specified array with the specified label. Each
+     * element is appended on its own line, and a new line is added after the
+     * last element.
+     */
+    void appendln(final String label, final Object[] objects);
+
+    /**
+     * Append the specified title, then start a new line. A title is shown on
+     * two lines with the text on the first line and dashes on the second.
+     */
+    void appendTitle(final String title);
+
+    void startSection(final String title);
+
+    void endSection();
+
+    /**
+     * Append a blank line only if there are existing lines and the previous
+     * line is not blank.
+     */
+    void blankLine();
+
+    /**
+     * Increase indent used when appending.
+     */
+    void indent();
+
+    /**
+     * Decrease indent used when appending.
+     */
+    void unindent();
+
+    void close();
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/debug/DebugHtmlString.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/debug/DebugHtmlString.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/debug/DebugHtmlString.java
new file mode 100644
index 0000000..54c10f5
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/debug/DebugHtmlString.java
@@ -0,0 +1,49 @@
+/*
+ *  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.isis.core.commons.debug;
+
+public class DebugHtmlString extends DebugHtmlStringAbstract {
+
+    StringBuffer debug = new StringBuffer();
+
+    public DebugHtmlString() {
+        super(false);
+    }
+
+    public DebugHtmlString(final boolean createPage) {
+        super(createPage);
+        header();
+    }
+
+    @Override
+    public void doClose() {
+        footer();
+    }
+
+    @Override
+    protected void appendHtml(final String html) {
+        debug.append(html);
+        debug.append("\n");
+    }
+
+    @Override
+    public String toString() {
+        return debug.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/debug/DebugHtmlStringAbstract.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/debug/DebugHtmlStringAbstract.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/debug/DebugHtmlStringAbstract.java
new file mode 100644
index 0000000..8b1b4cc
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/debug/DebugHtmlStringAbstract.java
@@ -0,0 +1,262 @@
+/*
+ *  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.isis.core.commons.debug;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+public abstract class DebugHtmlStringAbstract implements DebugBuilder {
+
+    private final boolean createPage;
+    private int tableLevel;
+    private boolean isOdd;
+    private boolean titleShown;
+    private boolean endLine;
+
+    public DebugHtmlStringAbstract(final boolean createPage) {
+        this.createPage = createPage;
+    }
+
+    @Override
+    public void concat(final DebugBuilder debug) {
+        appendHtml(debug.toString());
+    }
+
+    @Override
+    public void append(final int number, final int width) {
+        appendHtml(number + "");
+    }
+
+    @Override
+    public void append(final Object object) {
+        if (object instanceof DebuggableWithTitle) {
+            DebuggableWithTitle d = (DebuggableWithTitle) object;
+            appendTitle(d.debugTitle());
+            d.debugData(this);
+        } else {
+            appendHtml(object.toString());
+        }
+    }
+
+    @Override
+    public void append(final Object object, final int width) {
+        appendHtml(object.toString());
+    }
+
+    @Override
+    public void appendln() {
+        if (tableLevel > 0) {
+            endLine = true;
+        } else {
+            appendHtml("<p></p>");
+        }
+    }
+
+    @Override
+    public void blankLine() {
+        if (tableLevel > 0) {
+            appendHtml(row() + "<td class=\"error\" colspan=\"2\" >blank line</td></tr>");
+        } else {
+            appendHtml("<p>blank line</p>");
+        }
+    }
+
+    @Override
+    public void appendln(final String label, final boolean value) {
+        appendln(label, String.valueOf(value));
+    }
+
+    @Override
+    public void appendln(final String label, final double value) {
+        appendln(label, String.valueOf(value));
+    }
+
+    @Override
+    public void appendln(final String label, final long value) {
+        appendln(label, String.valueOf(value));
+    }
+
+    @Override
+    public void appendAsHexln(final String label, final long value) {
+        appendln(label, Long.toHexString(value));
+    }
+
+    @Override
+    public void appendPreformatted(final String label, final String object) {
+        final String value = object == null ? "null" : object.toString();
+        appendln(label, "<pre>" + value + "</pre>");
+    };
+
+    @Override
+    public void appendln(final String label, final Object object) {
+        final String value = object == null ? "null" : object.toString();
+        appendln(label, value);
+    }
+
+    @Override
+    public void appendln(final String label, final Object[] object) {
+        if (object.length == 0) {
+            appendln(label, "empty array");
+        } else {
+            appendln(label, object[0]);
+            for (int i = 1; i < object.length; i++) {
+                appendHtml(row() + "<td></td><td>" + object[i] + "</td></tr>");
+            }
+        }
+    }
+
+    @Override
+    public void startSection(final String title) {
+        startTableIfNeeded(true);
+        appendTitle(title);
+    }
+
+    @Override
+    public void endSection() {
+        endTableIfStarted();
+    }
+
+    @Override
+    public void indent() {
+        if (tableLevel > 0) {
+            appendHtml(row() + "<td>indented</td><td>");
+            startTableIfNeeded(true);
+        }
+    }
+
+    @Override
+    public void unindent() {
+        if (tableLevel > 0) {
+            endTableIfStarted();
+            appendHtml("</td>");
+        }
+    }
+
+    protected void header() {
+        if (createPage) {
+            appendHtml("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">");
+            appendHtml("<html>");
+            appendHtml("<head>");
+            appendHtml("<title>Debug Details</title>");
+            appendHtml("<style type=\"text/css\">");
+            appendHtml("body { margin: 15px; }\n" + ".links { background: #ddd; font-size: 80%; padding-bottom:5px; }\n" + ".links > p { display: inline-block; }\n" + "td {vertical-align: top; margin-left: 15px;}\n" + "td.error {color: red; font-style: italic; }\n" + "td.code {white-space: pre; font-family: monospace;}\n"
+                    + "th.title {text-align: left; padding: 0.3em 1em; font-style: italic; background: #AED1FF; }\n" + "td.label {width: 16em; text-align: right; padding-right: 1.5em; padding-top: 0.2em; font-size: 80%; font-weight: bold; }\n"
+                    + "span.facet-type { font-weight: bold; padding-right: 10px; }\n");
+            appendHtml("</style>");
+            appendHtml("</head>");
+            appendHtml("<body>");
+        }
+    }
+
+    protected abstract void appendHtml(String html);
+
+    protected void footer() {
+        endTableIfStarted();
+        if (createPage) {
+            appendHtml("</body>");
+            appendHtml("</html>");
+        }
+    }
+
+    @Override
+    public void appendException(final Throwable e) {
+        appendTitle("Exception");
+        final String message = e.getMessage();
+        if (message != null) {
+            appendHtml(row() + "<td class=\"error\" colspan=\"2\" >" + message + "</td></tr>");
+        }
+        causingException(e);
+        appendHtml(row() + "<td class=\"code\" colspan=\"2\" ><pre>");
+        final StringWriter stringWriter = new StringWriter();
+        final PrintWriter printWriter = new PrintWriter(stringWriter);
+        e.printStackTrace(printWriter);
+        appendHtml(stringWriter.toString());
+        appendHtml("</pre></td></tr>");
+
+    }
+
+    private void causingException(final Throwable throwable) {
+        final Throwable cause = throwable.getCause();
+        if (cause != null && cause != throwable) {
+            appendHtml(row() + "<td colspan=\"2\" >" + cause.getMessage() + "</td></tr>");
+            causingException(cause);
+        }
+    }
+
+    @Override
+    public void appendTitle(final String title) {
+        if (tableLevel > 0) {
+            String className = titleShown ? "subtitle" : "title";
+            appendHtml(row() + "<th class=\""+ className + "\" colspan=\"2\" >" + title + "</th></tr>");
+            titleShown = true;
+        } else {
+            appendHtml("<h2>" + title + "</h2>");
+        }
+    }
+
+    private void appendln(final String name, final String value) {
+        startTableIfNeeded(false);
+        appendHtml(row() + "<td class=\"label\">" + name + "</td><td>" + value + "</td></tr>");
+    }
+
+    private String row() {
+        final String line = (isOdd ? "odd" : "even") + (endLine ? " end-line" : ""); 
+        isOdd = !isOdd;
+        endLine = false;
+        return "<tr class=\"" + line + "\">";
+    }
+
+    private void startTableIfNeeded(final boolean b) {
+        if (tableLevel == 0 || b) {
+            appendHtml("<table class=\"debug\" summary=\"Debug details\" >");
+            tableLevel++;
+            titleShown = false;
+        }
+    }
+
+    private void endTableIfStarted() {
+        if (tableLevel > 0) {
+            appendHtml("</table>");
+            tableLevel--;
+        }
+    }
+
+    @Override
+    public void appendPreformatted(final String text) {
+        appendln("<pre>" + text + "</pre>");
+    }
+
+    @Override
+    public void appendln(final String text) {
+        if (tableLevel > 0) {
+            appendHtml(row() + "<td colspan=\"2\">" + text + "</td></tr>");
+        } else {
+            appendHtml("<p>" + text + "</p>");
+        }
+    }
+
+    @Override
+    public void close() {
+        endTableIfStarted();
+        doClose();
+    }
+
+    protected abstract void doClose();
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e4735c72/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/debug/DebugList.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/debug/DebugList.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/debug/DebugList.java
new file mode 100644
index 0000000..36e9b53
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/commons/debug/DebugList.java
@@ -0,0 +1,62 @@
+/*
+ *  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.isis.core.commons.debug;
+
+import java.util.List;
+
+import com.google.common.collect.Lists;
+
+public class DebugList {
+    private final List<String[]> names = Lists.newArrayList();
+    private final List<DebuggableWithTitle> debuggableList = Lists.newArrayList();
+
+    public DebugList(final String name) {
+        debuggableList.add(new DebuggableWithTitle() {
+            @Override
+            public void debugData(final DebugBuilder debug) {
+                for (String[] name : names) {
+                    debug.appendln(name[0], name[1]);
+                }
+            }
+
+            @Override
+            public String debugTitle() {
+                return name;
+            }
+        });
+    }
+
+    public void add(final String name, final Object object) {
+        final boolean b = object instanceof DebuggableWithTitle;
+        if (b) {
+            debuggableList.add((DebuggableWithTitle) object);
+        }
+        if (object != null) {
+            String[] n = new String[2];
+            n[0] = name + (b ? "*" : "");
+            n[1] = object.toString();
+            names.add(n);
+        }
+    }
+
+    public DebuggableWithTitle[] debug() {
+        return debuggableList.toArray(new DebuggableWithTitle[debuggableList.size()]);
+    }
+}