Closes #41

Move karaf distribution to brooklyn-dist.

This PR is the third of three that together move the karaf distribution to brooklyn-dist and separate out the software from brooklyn-library into separate catalog bom and feature files.

Merge this after https://github.com/apache/brooklyn-library/pull/53.
diff --git a/dist-karaf/apache-brooklyn/pom.xml b/dist-karaf/apache-brooklyn/pom.xml
new file mode 100755
index 0000000..087858d
--- /dev/null
+++ b/dist-karaf/apache-brooklyn/pom.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>apache-brooklyn</artifactId>
+  <packaging>karaf-assembly</packaging>
+  <name>Brooklyn Karaf Distro</name>
+
+  <parent>
+      <groupId>org.apache.brooklyn</groupId>
+      <artifactId>brooklyn-dist-karaf</artifactId>
+      <version>0.10.0-SNAPSHOT</version>  <!-- BROOKLYN_VERSION -->
+      <relativePath>../pom.xml</relativePath>
+  </parent>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.karaf.features</groupId>
+      <artifactId>framework</artifactId>
+      <version>${karaf.version}</version>
+      <type>kar</type>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.karaf.features</groupId>
+      <artifactId>standard</artifactId>
+      <classifier>features</classifier>
+      <version>${karaf.version}</version>
+      <type>xml</type>
+      <scope>runtime</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.karaf.features</groupId>
+      <artifactId>enterprise</artifactId>
+      <classifier>features</classifier>
+      <version>${karaf.version}</version>
+      <type>xml</type>
+      <scope>runtime</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.brooklyn</groupId>
+      <artifactId>brooklyn-features</artifactId>
+      <version>${project.version}</version>
+      <type>xml</type>
+      <classifier>features</classifier>
+      <scope>runtime</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.brooklyn</groupId>
+      <artifactId>brooklyn-library-features</artifactId>
+      <version>${project.version}</version>
+      <type>xml</type>
+      <classifier>features</classifier>
+      <scope>runtime</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.brooklyn</groupId>
+      <artifactId>brooklyn-library-catalog</artifactId>
+      <version>${project.version}</version>
+      <scope>runtime</scope>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+        <filtering>false</filtering>
+        <includes>
+         <include>**/*</include>
+        </includes>
+      </resource>
+      <resource>
+	<directory>src/main/filtered-resources</directory>
+	<filtering>true</filtering>
+	<includes>
+         <include>**/*</include>
+	</includes>
+      </resource>
+    </resources>
+
+    <plugins>
+      <plugin>
+          <groupId>org.apache.rat</groupId>
+          <artifactId>apache-rat-plugin</artifactId>
+          <configuration>
+              <excludes combine.children="append">
+                  <exclude>**/*.bom</exclude>
+              </excludes>
+          </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.karaf.tooling</groupId>
+        <artifactId>karaf-maven-plugin</artifactId>
+        <version>${karaf.version}</version>
+        <extensions>true</extensions>
+        <configuration>
+          <bootFeatures>
+            <bootFeature>aries-blueprint</bootFeature>
+            <bootFeature>bundle</bootFeature>
+            <bootFeature>config</bootFeature>
+            <bootFeature>deployer</bootFeature>
+            <bootFeature>diagnostic</bootFeature>
+            <bootFeature>feature</bootFeature>
+            <bootFeature>instance</bootFeature>
+            <bootFeature>jaas</bootFeature>
+            <bootFeature>kar</bootFeature>
+            <bootFeature>log</bootFeature>
+            <bootFeature>management</bootFeature>
+            <bootFeature>package</bootFeature>
+            <bootFeature>service</bootFeature>
+            <bootFeature>shell</bootFeature>
+            <bootFeature>shell-compat</bootFeature>
+            <bootFeature>ssh</bootFeature>
+            <bootFeature>system</bootFeature>
+            <bootFeature>wrap</bootFeature>
+            <!-- brooklyn features -->
+            <bootFeature>brooklyn-osgi-launcher</bootFeature>
+            <bootFeature>brooklyn-jsgui</bootFeature>
+            <bootFeature>brooklyn-rest-resources</bootFeature>
+            <bootFeature>brooklyn-commands</bootFeature>
+            <bootFeature>brooklyn-server-software-all</bootFeature>
+            <bootFeature>brooklyn-library-all</bootFeature>
+          </bootFeatures>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
+
diff --git a/dist-karaf/apache-brooklyn/src/main/filtered-resources/etc/branding.properties b/dist-karaf/apache-brooklyn/src/main/filtered-resources/etc/branding.properties
new file mode 100755
index 0000000..da2d564
--- /dev/null
+++ b/dist-karaf/apache-brooklyn/src/main/filtered-resources/etc/branding.properties
@@ -0,0 +1,35 @@
+################################################################################
+#
+#    Licensed to the Apache Software Foundation (ASF) under one or more
+#    contributor license agreements.  See the NOTICE file distributed with
+#    this work for additional information regarding copyright ownership.
+#    The ASF licenses this file to You under the Apache License, Version 2.0
+#    (the "License"); you may not use this file except in compliance with
+#    the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+#
+################################################################################
+
+welcome = \
+\u001B[33m\u001B[0m\n\
+\u001B[33m  _                     _    _              \u001B[0m\n\
+\u001B[33m | |__  _ __ ___   ___ | | _| |_   _ _ __ (R) \u001B[0m\n\
+\u001B[33m | '_ \\| '__/ _ \\ / _ \\| |/ / | | | | '_ \\  \u001B[0m\n\
+\u001B[33m | |_) | | | (_) | (_) |   <| | |_| | | | |  \u001B[0m\n\
+\u001B[33m |_.__/|_|  \\___/ \\___/|_|\\_\\_|\\__, |_| |_|  \u001B[0m\n\
+\u001B[33m                               |___/              \u001B[0m\n\
+\u001B[33m                                             \u001B[0m\n\
+\u001B[33m http://brooklyn.apache.org \u001B[0m\n\
+\u001B[33m (version ${project.version})\u001B[0m\n\
+\u001B[33m\u001B[0m\n\
+\u001B[33mHit '\u001B[1m<tab>\u001B[0m' for a list of available commands\u001B[0m\n\
+\u001B[33mand '\u001B[1m[cmd] --help\u001B[0m' for help on a specific command.\u001B[0m\n\
+\u001B[33mHit '\u001B[1m<ctrl-d>\u001B[0m' or '\u001B[1mosgi:shutdown\u001B[0m' to shutdown\u001B[0m\n\
+\u001B[33m\u001B[0m\n\
diff --git a/dist-karaf/apache-brooklyn/src/main/resources/etc/custom.properties b/dist-karaf/apache-brooklyn/src/main/resources/etc/custom.properties
new file mode 100644
index 0000000..7da5c63
--- /dev/null
+++ b/dist-karaf/apache-brooklyn/src/main/resources/etc/custom.properties
@@ -0,0 +1,120 @@
+################################################################################
+#
+#    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.
+#
+################################################################################
+
+#
+# You can place any customized configuration here.
+# All the values specified here will override the default value.
+#
+
+# Brooklyn used to bundle Apache Felix, so force felix
+karaf.framework=felix
+
+# brooklyn used 4.4.0 at the time of osgification, which differs from karaf's bundled felix version
+#karaf.framework.felix=mvn\:org.apache.felix/org.apache.felix.framework/${felix.framework.version}
+
+#karaf.systemBundlesStartLevel=50
+#
+#org.osgi.framework.bootdelegation=org.apache.karaf.jaas.boot,!com.sun.xml.messaging.saaj.*,!com.sun.xml.internal.bind.*,sun.*,com.sun.*,javax.transaction,javax.transaction.*,org.apache.xalan.processor,org.apache.xpath.jaxp,org.apache.xml.dtm.ref,org.apache.xerces.jaxp.datatype,org.apache.xerces.stax,org.apache.xerces.parsers,org.apache.xerces.jaxp,org.apache.xerces.jaxp.validation,org.apache.xerces.dom
+#
+#org.osgi.framework.system.packages.extra = \
+#  org.apache.karaf.branding, \
+#  com.sun.org.apache.xalan.internal.xsltc.trax, \
+#  com.sun.org.apache.xerces.internal.dom, \
+#  com.sun.org.apache.xerces.internal.jaxp, \
+#  com.sun.org.apache.xerces.internal.xni, \
+#  com.sun.jndi.ldap, \
+#  org.apache.xalan.extensions; version="2.7.1", \
+#  org.apache.xalan.xsltc.compiler; version="2.7.1", \
+#  org.apache.xalan.xsltc.cmdline.getopt; version="2.7.1", \
+#  org.apache.xalan.xsltc.util; version="2.7.1", \
+#  org.apache.xalan.transformer; version="2.7.1", \
+#  org.apache.xalan.xsltc.trax; version="2.7.1", \
+#  org.apache.xalan.processor; version="2.7.1", \
+#  org.apache.xalan.lib; version="2.7.1", \
+#  org.apache.xalan.trace; version="2.7.1", \
+#  org.apache.xalan.xsltc.compiler.util; version="2.7.1", \
+#  org.apache.xalan.templates; version="2.7.1", \
+#  org.apache.xalan.xsltc; version="2.7.1", \
+#  org.apache.xalan.xsltc.runtime; version="2.7.1", \
+#  org.apache.xalan; version="2.7.1", \
+#  org.apache.xalan.xslt; version="2.7.1", \
+#  org.apache.xalan.lib.sql; version="2.7.1", \
+#  org.apache.xalan.xsltc.runtime.output; version="2.7.1", \
+#  org.apache.xalan.xsltc.dom; version="2.7.1", \
+#  org.apache.xalan.client; version="2.7.1", \
+#  org.apache.xalan.xsltc.cmdline; version="2.7.1", \
+#  org.apache.xalan.serialize; version="2.7.1", \
+#  org.apache.xalan.res; version="2.7.1", \
+#  org.apache.xml.dtm.ref; version="2.7.1", \
+#  org.apache.xml.dtm; version="2.7.1", \
+#  org.apache.xml.dtm.ref.sax2dtm; version="2.7.1", \
+#  org.apache.xml.dtm.ref.dom2dtm; version="2.7.1", \
+#  org.apache.xml.res; version="2.7.1", \
+#  org.apache.xml.serializer.dom3; version="2.7.1", \
+#  org.apache.xml.serializer; version="2.7.1", \
+#  org.apache.xml.serializer.utils; version="2.7.1", \
+#  org.apache.xml.utils.res; version="2.7.1", \
+#  org.apache.xml.utils; version="2.7.1", \
+#  org.apache.xpath.functions; version="2.7.1", \
+#  org.apache.xpath.jaxp; version="2.7.1", \
+#  org.apache.xpath.patterns; version="2.7.1", \
+#  org.apache.xpath.objects; version="2.7.1", \
+#  org.apache.xpath.res; version="2.7.1", \
+#  org.apache.xpath; version="2.7.1", \
+#  org.apache.xpath.axes; version="2.7.1", \
+#  org.apache.xpath.compiler; version="2.7.1", \
+#  org.apache.xpath.operations; version="2.7.1", \
+#  org.apache.xpath.domapi; version="2.7.1", \
+#  org.apache.html.dom; version="2.11.0", \
+#  org.apache.wml.dom; version="2.11.0", \
+#  org.apache.wml; version="2.11.0", \
+#  org.apache.xerces.parsers; version="2.11.0", \
+#  org.apache.xerces.impl.dtd.models; version="2.11.0", \
+#  org.apache.xerces.xni.parser; version="2.11.0", \
+#  org.apache.xerces.impl.dv.xs; version="2.11.0", \
+#  org.apache.xerces.impl.xs.traversers; version="2.11.0", \
+#  org.apache.xerces.util; version="2.11.0", \
+#  org.apache.xerces.impl.dtd; version="2.11.0", \
+#  org.apache.xerces.jaxp.validation; version="2.11.0", \
+#  org.apache.xerces.dom3.as; version="2.11.0", \
+#  org.apache.xerces.impl.dv; version="2.11.0", \
+#  org.apache.xerces.jaxp; version="2.11.0", \
+#  org.apache.xerces.jaxp.datatype; version="2.11.0", \
+#  org.apache.xerces.impl.xpath.regex; version="2.11.0", \
+#  org.apache.xerces.xni; version="2.11.0", \
+#  org.apache.xerces.impl.msg; version="2.11.0", \
+#  org.apache.xerces.impl.dv.util; version="2.11.0", \
+#  org.apache.xerces.impl.xs.util; version="2.11.0", \
+#  org.apache.xerces.dom; version="2.11.0", \
+#  org.apache.xerces.dom.events; version="2.11.0", \
+#  org.apache.xerces.impl.xs.opti; version="2.11.0", \
+#  org.apache.xerces.impl; version="2.11.0", \
+#  org.apache.xerces.xs; version="2.11.0", \
+#  org.apache.xerces.impl.io; version="2.11.0", \
+#  org.apache.xerces.xpointer; version="2.11.0", \
+#  org.apache.xerces.impl.dv.dtd; version="2.11.0", \
+#  org.apache.xerces.xinclude; version="2.11.0", \
+#  org.apache.xerces.impl.xpath; version="2.11.0", \
+#  org.apache.xerces.xs.datatypes; version="2.11.0", \
+#  org.apache.xerces.impl.xs.identity; version="2.11.0", \
+#  org.apache.xerces.impl.xs.models; version="2.11.0", \
+#  org.apache.xerces.xni.grammars; version="2.11.0", \
+#  org.apache.xerces.impl.xs; version="2.11.0", \
+#  org.apache.xerces.impl.validation; version="2.11.0", \
+#  org.apache.xml.serialize; version="2.11.0"
\ No newline at end of file
diff --git a/dist-karaf/apache-brooklyn/src/main/resources/etc/default.catalog.bom b/dist-karaf/apache-brooklyn/src/main/resources/etc/default.catalog.bom
new file mode 100644
index 0000000..54424fe
--- /dev/null
+++ b/dist-karaf/apache-brooklyn/src/main/resources/etc/default.catalog.bom
@@ -0,0 +1,368 @@
+
+# this catalog bom is an illustration supplying a few useful sample items
+# and templates to get started using Brooklyn
+
+brooklyn.catalog:
+  version: "0.10.0-SNAPSHOT" # BROOKLYN_VERSION
+  include: classpath://library-catalog-classes.bom
+
+  items:
+
+  - id: server
+    itemType: entity
+    description: |
+      Provision a server, with customizable provisioning.properties and credentials installed, 
+      but no other special software process or scripts executed.
+    item:
+      type: org.apache.brooklyn.entity.software.base.EmptySoftwareProcess
+      name: Server
+
+  - id: vanilla-bash-server
+    itemType: entity
+    description: |
+      Provision a server, with customizable provisioning.properties and credentials installed, 
+      but no other special software process or scripts executed.
+      The script should be supplied in "launch.command" as per docs on
+      org.apache.brooklyn.entity.software.base.VanillaSoftwareProcess.
+    item:
+      type: org.apache.brooklyn.entity.software.base.VanillaSoftwareProcess
+      name: Server with Launch Script (bash)
+
+  - id: load-balancer
+    itemType: entity
+    description: |
+      Create a load balancer which will point at members in the group entity
+      referred to by the config key "serverPool". 
+      The sensor advertising the port can be configured with the "member.sensor.portNumber" config key,
+      defaulting to `http.port`; all member entities which have published "service.up" will then be picked up.
+    item:
+      type: org.apache.brooklyn.entity.proxy.nginx.NginxController
+      name: Load Balancer (nginx)
+
+  - id: cluster
+    itemType: entity
+    description: |
+      Create a cluster of entities, resizable, with starting size "initialSize",
+      and using a spec supplied in the "memberSpec" key.
+    item:
+      type: org.apache.brooklyn.entity.group.DynamicCluster
+
+  - id: 1-server-template
+    itemType: template
+    name: "Template 1: Server"
+    description: |
+      Sample YAML to provision a server in a cloud with illustrative VM properties
+    item:
+      name: Server (Brooklyn Example)
+      
+      # this basic example shows how Brooklyn can provision a single raw VM
+      # in the cloud or location of your choice
+      
+      services:
+      - type:           server
+        name:           My VM
+      
+      # location can be e.g. `softlayer` or `jclouds:openstack-nova:https://9.9.9.9:9999/v2.0/`,
+      # or `localhost` or `byon:(hosts="10.9.1.1,10.9.1.2,produser2@10.9.2.{10,11,20-29}")` 
+      location:
+        jclouds:aws-ec2:
+          # edit these to use your credential (or delete if credentials specified in brooklyn.properties)      
+          identity:     <REPLACE>
+          credential:   <REPLACE>
+          
+          region:       eu-central-1
+          
+          # we want Ubuntu, with a lot of RAM
+          osFamily:     ubuntu
+          minRam:       8gb
+          
+          # set up this user and password (default is to authorize a public key)
+          user:         sample
+          password:     s4mpl3
+
+  - id: 2-bash-web-server-template
+    itemType: template
+    name: "Template 2: Bash Web Server"
+    description: |
+      Sample YAML building on Template 1, 
+      adding bash commands to launch a Python-based web server
+      on port 8020
+    item:
+      name: Python Web Server (Brooklyn Example)
+      
+      # this example builds on the previous one, 
+      # adding some scripts to initialize the VM
+      
+      services:
+      - type:           vanilla-bash-server
+        name:           My Bash Web Server VM
+        brooklyn.config:
+          install.command: |
+            # install python if not present
+            which python || \
+              { sudo apt-get update && sudo apt-get install python ; } || \
+              { sudo yum update && sudo yum install python ; } || \
+              { echo WARNING: cannot install python && exit 1 ; }
+
+          customize.command: |
+            # create the web page to serve
+            cat > index.html << EOF
+            
+            Hello world.
+            <p>
+            I am ${ENTITY_INFO}, ${MESSAGE:-a Brooklyn sample}.
+            <p>
+            Created at: `date`
+            <p>
+            I am running at ${HOSTNAME}, with on-box IP configuration:
+            <pre>
+            `ifconfig | grep inet`
+            </pre>
+            
+            EOF
+
+          launch.command: |
+            # launch in background (ensuring no streams open), and record PID to file
+            nohup python -m SimpleHTTPServer ${PORT:-8020} < /dev/null > output.txt 2>&1 &
+            echo $! > ${PID_FILE:-pid.txt}
+            sleep 5
+            ps -p `cat ${PID_FILE:-pid.txt}`
+            if [ $? -ne 0 ] ; then
+              cat output.txt
+              echo WARNING: python web server not running
+              exit 1
+            fi
+            
+          shell.env:
+            HOSTNAME:     $brooklyn:attributeWhenReady("host.name")
+            PORT:         $brooklyn:config("my.app.port")
+            ENTITY_INFO:  $brooklyn:component("this", "")
+            MESSAGE:      $brooklyn:config("my.message")
+            
+          # custom 
+          my.app.port:  8020
+          my.message:   "good to meet you"
+        
+        brooklyn.enrichers:
+        # publish the URL as a sensor; the GUI will pick this up (main.uri)
+        - type: org.apache.brooklyn.enricher.stock.Transformer
+          brooklyn.config:
+            uniqueTag: url-generator
+            enricher.sourceSensor: host.subnet.hostname
+            # use the definition from Attributes class, as it has a RendererHint so GUI makes it a link
+            enricher.targetSensor: $brooklyn:sensor("org.apache.brooklyn.core.entity.Attributes", "main.uri")
+            enricher.targetValue: 
+              $brooklyn:formatString:
+              - "http://%s:%s/" 
+              - $brooklyn:attributeWhenReady("host.subnet.hostname")
+              - $brooklyn:config("my.app.port")
+      
+      location:
+        jclouds:aws-ec2:
+          region:       eu-central-1
+          # edit these (or delete if credentials specified in brooklyn.properties)      
+          identity:     <REPLACE>
+          credential:   <REPLACE>
+        
+  - id: 3-bash-web-and-riak-template
+    itemType: template
+    name: "Template 3: Bash Web Server and Scaling Riak Cluster"
+    description: |
+      Sample YAML building on Template 2, 
+      composing that blueprint with a Riak cluster and injecting the URL
+    item:
+      name: Bash Web Server and Riak Cluster (Brooklyn Example)
+    
+      # this example *references* the previous one, 
+      # combining it with a stock blueprint for a Riak cluster,
+      # and shows how a sensor from the latter can be injected
+      
+      services:
+      
+      # reference template 2, overriding message to point at riak 
+      - type:           2-bash-web-server-template
+        brooklyn.config:
+          my.message:   $brooklyn:formatString("connected to Riak at %s",
+                            $brooklyn:entity("riak-cluster").attributeWhenReady("main.uri"))
+        # and clear the location defined there so it is taken from this template
+        locations:      []
+                            
+      # use the off-the-shelf Riak cluster
+      - type:           org.apache.brooklyn.entity.nosql.riak.RiakCluster
+        id:             riak-cluster
+        initialSize:    3
+        # and add a policy to scale based on ops per minute
+        brooklyn.policies:
+        - type: org.apache.brooklyn.policy.autoscaling.AutoScalerPolicy
+          brooklyn.config:
+            metric: riak.node.ops.1m.perNode
+            # more than 100 ops per second (6k/min) scales out, less than 50 scales back
+            # up to a max of 8 riak nodes here (can be changed in GUI / REST API afterwards)
+            metricLowerBound: 3000
+            metricUpperBound: 6000
+            minPoolSize: 3
+            maxPoolSize: 8
+            resizeUpStabilizationDelay: 30s
+            resizeDownStabilizationDelay: 5m
+          
+      location:
+        jclouds:aws-ec2:
+          region:       eu-central-1
+          # edit these (or delete if credentials specified in brooklyn.properties)      
+          identity:     <REPLACE>
+          credential:   <REPLACE>
+
+  - id: 4-resilient-bash-web-cluster-template
+    itemType: template
+    name: "Template 4: Resilient Load-Balanced Bash Web Cluster with Sensors"
+    description: |
+      Sample YAML to provision a cluster of the bash/python web server nodes,
+      with sensors configured, and a load balancer pointing at them,
+      and resilience policies for node replacement and scaling
+    item:
+      name: Resilient Load-Balanced Bash Web Cluster (Brooklyn Example)
+      
+      # this final example shows some of the advanced functionality:
+      # defining custom sensors, and a cluster with a "spec", 
+      # policies for resilience and scaling based on that sensor,
+      # and wiring a load balancer in front of the cluster
+      
+      # combining this with the riak cluster in the previous example
+      # is left as a suggested exercise for the user
+      
+      services:
+      
+      # define a cluster of the web nodes
+      - type:           cluster
+        name:           Cluster of Bash Web Nodes
+        id:             my-web-cluster
+        brooklyn.config:
+          initialSize:  1
+          memberSpec:
+            $brooklyn:entitySpec:
+              # template 2 is used as the spec for items in this cluster
+              # with a new message overwriting the previous,
+              # and a lot of sensors defined
+              type:           2-bash-web-server-template
+              name:           My Bash Web Server VM with Sensors
+              # and clear the location defined there so it is taken from this template
+              locations:      []
+              
+              brooklyn.config:
+                my.message:   "part of the cluster"
+              
+              brooklyn.initializers:
+              # make a simple request-count sensor, by counting the number of 200 responses in output.txt
+              - type: org.apache.brooklyn.core.sensor.ssh.SshCommandSensor
+                brooklyn.config:
+                  name: reqs.count
+                  targetType: int
+                  period: 5s
+                  command: "cat output.txt | grep HTTP | grep 200 | wc | awk '{print $1}'"
+              # and publish the port as a sensor so the load-balancer can pick it up
+              - type:           org.apache.brooklyn.core.sensor.StaticSensor
+                brooklyn.config:
+                  name:         app.port
+                  targetType:   int
+                  static.value: $brooklyn:config("my.app.port")
+              
+              brooklyn.enrichers:
+              # derive reqs.per_sec from reqs.count
+              - type: org.apache.brooklyn.enricher.stock.YamlTimeWeightedDeltaEnricher
+                brooklyn.config:
+                  enricher.sourceSensor: reqs.count
+                  enricher.targetSensor: reqs.per_sec
+                  enricher.delta.period: 1s
+              # and take an average over 30s for reqs.per_sec into reqs.per_sec.windowed_30s
+              - type: org.apache.brooklyn.enricher.stock.YamlRollingTimeWindowMeanEnricher
+                brooklyn.config:
+                  enricher.sourceSensor: reqs.per_sec
+                  enricher.targetSensor: reqs.per_sec.windowed_30s
+                  enricher.window.duration: 30s
+              
+              # emit failure sensor if a failure connecting to the service is sustained for 30s
+              - type: org.apache.brooklyn.policy.ha.ServiceFailureDetector
+                brooklyn.config:
+                  entityFailed.stabilizationDelay: 30s
+            
+              brooklyn.policies:
+              # restart if a failure is detected (with a max of one restart in 2m, sensor will propagate otherwise) 
+              - type: org.apache.brooklyn.policy.ha.ServiceRestarter
+                brooklyn.config:
+                  failOnRecurringFailuresInThisDuration: 2m
+        
+        # back at the cluster, create a total per-sec and some per-node average
+        brooklyn.enrichers:
+        - type: org.apache.brooklyn.enricher.stock.Aggregator
+          brooklyn.config:
+            enricher.sourceSensor: reqs.per_sec
+            enricher.targetSensor: reqs.per_sec
+            transformation: sum
+        - type: org.apache.brooklyn.enricher.stock.Aggregator
+          brooklyn.config:
+            enricher.sourceSensor: reqs.per_sec
+            enricher.targetSensor: reqs.per_sec.per_node
+            transformation: average
+        - type: org.apache.brooklyn.enricher.stock.Aggregator
+          brooklyn.config:
+            enricher.sourceSensor: reqs.per_sec.windowed_30s
+            enricher.targetSensor: reqs.per_sec.windowed_30s.per_node
+            transformation: average
+              
+        brooklyn.policies:
+        # resilience: if a per-node restart policy fails,
+        # just throw that node away and create a new one
+        - type: org.apache.brooklyn.policy.ha.ServiceReplacer
+        
+        # and scale based on reqs/sec
+        - type: org.apache.brooklyn.policy.autoscaling.AutoScalerPolicy
+          brooklyn.config:
+            # scale based on reqs/sec (though in a real-world situation, 
+            # reqs.per_sec.windowed_30s.per_node might be a better choice) 
+            metric: reqs.per_sec.per_node
+            
+            # really low numbers, so you can trigger a scale-out just by hitting reload a lot
+            metricUpperBound: 3
+            metricLowerBound: 1
+            
+            # sustain 3 reqs/sec for 2s and it will scale out
+            resizeUpStabilizationDelay: 2s
+            # only scale down when sustained for 1m
+            resizeDownStabilizationDelay: 1m
+      
+            maxPoolSize: 10
+            
+      # and add a load-balancer pointing at the cluster
+      - type:           load-balancer
+        id:             load-bal
+        brooklyn.config:
+          # point this load balancer at the cluster, specifying port to forward to
+          loadbalancer.serverpool:  $brooklyn:entity("my-web-cluster")
+          member.sensor.portNumber: app.port
+          # disable sticky sessions to allow easy validation of balancing via browser refresh
+          nginx.sticky: false
+      
+      brooklyn.enrichers:
+      # publish a few useful info sensors and KPI's to the root of the app
+      - type: org.apache.brooklyn.enricher.stock.Propagator
+        brooklyn.config:
+          uniqueTag:    propagate-load-balancer-url
+          producer:     $brooklyn:entity("load-bal")
+          propagating:
+          - main.uri
+      - type: org.apache.brooklyn.enricher.stock.Propagator
+        brooklyn.config:
+          uniqueTag:    propagate-reqs-per-sec
+          producer:     $brooklyn:entity("my-web-cluster")
+          propagating:
+          - reqs.per_sec
+          - reqs.per_sec.windowed_30s.per_node
+      
+      location:
+        jclouds:aws-ec2:
+          # edit these (or delete if credentials specified in brooklyn.properties)      
+          identity:     <REPLACE>
+          credential:   <REPLACE>
+          
+          region:       eu-central-1
+          minRam:       2gb
diff --git a/dist-karaf/apache-brooklyn/src/main/resources/etc/org.apache.brooklyn.core.catalog.bomscanner.cfg b/dist-karaf/apache-brooklyn/src/main/resources/etc/org.apache.brooklyn.core.catalog.bomscanner.cfg
new file mode 100644
index 0000000..242dfd6
--- /dev/null
+++ b/dist-karaf/apache-brooklyn/src/main/resources/etc/org.apache.brooklyn.core.catalog.bomscanner.cfg
@@ -0,0 +1,27 @@
+################################################################################
+#
+#    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.
+#
+################################################################################
+
+# CSV Whitelist of regular expressions to match bundle symbolic id if bundle is to be permitted to add
+# applications (templates) to the catalog
+whiteList=.*
+
+# CSV Blacklist of regular expressions to match bundle symbolic id to prevent selected whitelisted bundles
+# adding applications (templates) to the catalog
+blackList=
+
diff --git a/dist-karaf/apache-brooklyn/src/main/resources/etc/org.apache.brooklyn.osgilauncher.cfg b/dist-karaf/apache-brooklyn/src/main/resources/etc/org.apache.brooklyn.osgilauncher.cfg
new file mode 100644
index 0000000..24a619a
--- /dev/null
+++ b/dist-karaf/apache-brooklyn/src/main/resources/etc/org.apache.brooklyn.osgilauncher.cfg
@@ -0,0 +1,65 @@
+################################################################################
+#
+#    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.
+#
+################################################################################
+
+# This file temporarily contains boot settings for brooklyn karaf launcher,
+# matching some of the command-line options of the previous brooklyn-cli launcher
+# (those used during the initialization sequence)
+#
+# Most of these will migrate to a permanent cfg file, once the components themselves get refactored
+# and the configuration options are agreed upon
+
+# Location of the global brooklyn.properties file
+#globalBrooklynPropertiesFile=~/.brooklyn/brooklyn.properties
+
+# Location of the local brooklyn.properties file, normally specified at the cli. Overrides properties from the global set.
+#localBrooklynPropertiesFile=
+
+
+# Ignore catalog subsystem failures during startup (default is to continue, so errors can be viewed via the API)
+#ignoreCatalogErrors=true
+
+# Ignore persistence/HA subsystem failures during startup (default is to continue, so errors can be viewed via the API)
+#ignorePersistenceErrors=true
+
+# The high availability mode. Possible values are:
+# - DISABLED: management node works in isolation - will not cooperate with any other standby/master nodes in management plane;
+# - AUTO: will look for other management nodes, and will allocate itself as standby or master based on other nodes' states;
+# - MASTER: will startup as master - if there is already a master then fails immediately;
+# - STANDBY: will start up as lukewarm standby with no state - if there is not already a master then fails immediately,
+#   and if there is a master which subsequently fails, this node can promote itself;
+# - HOT_STANDBY: will start up as hot standby in read-only mode - if there is not already a master then fails immediately,
+#   and if there is a master which subseuqently fails, this node can promote itself;
+# - HOT_BACKUP: will start up as hot backup in read-only mode - no master is required, and this node will not become a master
+#highAvailabilityMode=DISABLED
+
+# The persistence mode. Possible values are:
+# - DISABLED: will not read or persist any state;
+# - AUTO: will rebind to any existing state, or start up fresh if no state;
+# - REBIND: will rebind to the existing state, or fail if no state available;
+# - CLEAN: will start up fresh (removing any existing state)
+#persistMode=DISABLED
+
+# The directory to read/write persisted state (or container name if using an object store)
+#persistenceDir=
+
+# The location spec for an object store to read/write persisted state
+#persistenceLocation=
+
+# Periodic read-only rebind
+#persistPeriod=1s
diff --git a/dist-karaf/apache-brooklyn/src/main/resources/etc/org.ops4j.pax.logging.cfg b/dist-karaf/apache-brooklyn/src/main/resources/etc/org.ops4j.pax.logging.cfg
new file mode 100755
index 0000000..acd2aea
--- /dev/null
+++ b/dist-karaf/apache-brooklyn/src/main/resources/etc/org.ops4j.pax.logging.cfg
@@ -0,0 +1,95 @@
+################################################################################
+#
+#    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.
+#
+################################################################################
+
+# Root logger
+log4j.rootLogger=INFO, out, debugFile, osgi:VmLogAppender
+log4j.throwableRenderer=org.apache.log4j.OsgiThrowableRenderer
+
+# CONSOLE appender not used by default
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %-5.5p %3.3X{bundle.id} %-30.30c{2} [%-16.16t] %m%n
+
+# Info file appender
+log4j.appender.out=org.apache.log4j.RollingFileAppender
+log4j.appender.out.layout=org.apache.log4j.PatternLayout
+log4j.appender.out.layout.ConversionPattern=%d{ABSOLUTE} %-5.5p %3.3X{bundle.id} %-30.30c{2} [%-16.16t] %m%n
+log4j.appender.out.file=${karaf.home}/log/brooklyn.info.log
+log4j.appender.out.Threshold=INFO
+log4j.appender.out.append=true
+log4j.appender.out.maxFileSize=100MB
+log4j.appender.out.maxBackupIndex=10
+
+# Debug file appender
+log4j.appender.debugFile=org.apache.log4j.RollingFileAppender
+log4j.appender.debugFile.layout=org.apache.log4j.PatternLayout
+log4j.appender.debugFile.layout.ConversionPattern=%d{ABSOLUTE} %-5.5p %3.3X{bundle.id} %-30.30c{2} [%-16.16t] %m%n
+log4j.appender.debugFile.file=${karaf.home}/log/brooklyn.debug.log
+log4j.appender.debugFile.append=true
+log4j.appender.debugFile.maxFileSize=100MB
+log4j.appender.debugFile.maxBackupIndex=10
+
+# Sift appender
+log4j.appender.sift=org.apache.log4j.sift.MDCSiftingAppender
+log4j.appender.sift.key=bundle.name
+log4j.appender.sift.default=brooklyn
+log4j.appender.sift.appender=org.apache.log4j.FileAppender
+log4j.appender.sift.appender.layout=org.apache.log4j.PatternLayout
+log4j.appender.sift.appender.layout.ConversionPattern=%d{ABSOLUTE} %-5.5p %3.3X{bundle.id} %-30.30c{2} [%-16.16t] %m%n
+log4j.appender.sift.appender.file=${karaf.data}/log/$\\{bundle.name\\}.log
+log4j.appender.sift.appender.append=true
+
+
+# Logger configuration
+
+# The following properties turn on quite verbose DEBUG logging for Brooklyn-relevant loggers
+log4j.logger.brooklyn=DEBUG
+log4j.logger.org.apache.brooklyn=DEBUG
+log4j.logger.org.jclouds=DEBUG
+log4j.logger.jclouds=DEBUG
+io.cloudsoft.winrm4j.winrm.WinRmTool=DEBUG
+
+# If you're just going to have a few debug categories these are recommended;
+# comment out the loggers above and leave these as-is
+log4.logger.brooklyn.SSH=DEBUG
+log4.logger.brooklyn.location.basic.jclouds=DEBUG
+log4.logger.brooklyn.util.internal.ssh=DEBUG
+log4.logger.org.apache.brooklyn.SSH=DEBUG
+log4.logger.org.apache.brooklyn.location.basic.jclouds=DEBUG
+log4.logger.org.apache.brooklyn.util.internal.ssh=DEBUG
+
+# a bit noisy at INFO, but still, poss interesting
+log4j.logger.org.reflections.Reflections=INFO
+log4j.logger.com.sun.jersey.server.impl.application=INFO
+log4j.logger.org.apache.whirr.service.ComputeCache=INFO
+log4j.logger.jclouds.ssh=INFO
+log4j.logger.org.apache.http.impl.client=INFO
+log4j.logger.javax.management.remote=INFO
+
+# some loggers are very noisy however, exclude them
+log4j.logger.org.apache.cxf=WARN
+log4j.logger.net.schmizz=WARN
+log4j.logger.org.eclipse.jetty=WARN
+log4j.logger.org.mongodb.driver=WARN
+
+#  Wordnik logs errors in a few places which aren't errors at all; ignore them altogether
+# (Turn them back on if you need to see how API-doc gets generated, and also
+# see https://github.com/wordnik/swagger-core/issues/58)
+log4j.logger.com.wordnik.swagger=OFF
+
diff --git a/dist-karaf/apache-brooklyn/src/main/resources/etc/org.ops4j.pax.url.mvn.repositories.cfg b/dist-karaf/apache-brooklyn/src/main/resources/etc/org.ops4j.pax.url.mvn.repositories.cfg
new file mode 100644
index 0000000..b7bc8c9
--- /dev/null
+++ b/dist-karaf/apache-brooklyn/src/main/resources/etc/org.ops4j.pax.url.mvn.repositories.cfg
@@ -0,0 +1,20 @@
+################################################################################
+#
+#    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.
+#
+################################################################################
+
+org.ops4j.pax.url.mvn.repositories=+http://repo1.maven.org/maven2
diff --git a/dist-karaf/apache-brooklyn/src/main/resources/etc/org.ops4j.pax.web.cfg b/dist-karaf/apache-brooklyn/src/main/resources/etc/org.ops4j.pax.web.cfg
new file mode 100644
index 0000000..39947eb
--- /dev/null
+++ b/dist-karaf/apache-brooklyn/src/main/resources/etc/org.ops4j.pax.web.cfg
@@ -0,0 +1,19 @@
+#
+#    Licensed to the Apache Software Foundation (ASF) under one or more
+#    contributor license agreements.  See the NOTICE file distributed with
+#    this work for additional information regarding copyright ownership.
+#    The ASF licenses this file to You under the Apache License, Version 2.0
+#    (the "License"); you may not use this file except in compliance with
+#    the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+#
+# TODO use the PortService - ${port:8081,8200}
+org.osgi.service.http.port=8081
+
diff --git a/dist-karaf/apache-brooklyn/src/main/resources/etc/system.properties b/dist-karaf/apache-brooklyn/src/main/resources/etc/system.properties
new file mode 100644
index 0000000..a95f167
--- /dev/null
+++ b/dist-karaf/apache-brooklyn/src/main/resources/etc/system.properties
@@ -0,0 +1,133 @@
+################################################################################
+#
+#    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.
+#
+################################################################################
+
+#
+# The properties defined in this file will be made available through system
+# properties at the very beginning of the Karaf's boot process.
+#
+
+
+# Log level when the pax-logging service is not available
+# This level will only be used while the pax-logging service bundle
+# is not fully available.
+# To change log levels, please refer to the org.ops4j.pax.logging.cfg file
+# instead.
+org.ops4j.pax.logging.DefaultServiceLog.level = ERROR
+
+#
+# Name of this Karaf instance.
+#
+karaf.name = brooklyn
+
+#
+# Default repository where bundles will be loaded from before using
+# other Maven repositories.  For the full Maven configuration, see
+# the org.ops4j.pax.url.mvn.cfg file.
+#
+karaf.default.repository = system
+
+#
+# Location of a shell script that will be run when starting a shell
+# session.  This script can be used to create aliases and define
+# additional commands.
+#
+karaf.shell.init.script = ${karaf.etc}/shell.init.script
+
+#
+# Sets the maximum size of the shell command history. If not set,
+# defaults to 500 entries. Setting to 0 will disable history.
+#
+# karaf.shell.history.maxSize = 0
+
+#
+# Deletes the entire karaf.data directory at every start
+#
+karaf.clean.all = false
+
+#
+# Deletes the karaf.data/cache directory at every start
+#
+karaf.clean.cache = false
+
+#
+# Roles to use when logging into a local Karaf console.
+#
+# The syntax is the following:
+#   [classname:]principal
+# where classname is the class name of the principal object
+# (defaults to org.apache.karaf.jaas.modules.RolePrincipal)
+# and principal is the name of the principal of that class
+# (defaults to instance).
+#
+karaf.local.roles = admin,manager,viewer,systembundles
+
+#
+# Set this empty property to avoid errors when validating xml documents.
+#
+xml.catalog.files =
+
+#
+# Suppress the bell in the console when hitting backspace too many times
+# for example
+#
+jline.nobell = true
+
+#
+# ServiceMix specs options
+#
+org.apache.servicemix.specs.debug = false
+org.apache.servicemix.specs.timeout = 0
+
+#
+# Settings for the OSGi 4.3 Weaving
+# By default, we will not weave any classes. Change this setting to include classes
+# that you application needs to have woven.
+#
+org.apache.aries.proxy.weaving.enabled = none
+# Classes not to weave - Aries default + Xerces which is known to have issues.
+org.apache.aries.proxy.weaving.disabled = org.objectweb.asm.*,org.slf4j.*,org.apache.log4j.*,javax.*,org.apache.xerces.*
+
+#
+# By default, only Karaf shell commands are secured, but additional services can be
+# secured by expanding this filter
+#
+karaf.secured.services = (&(osgi.command.scope=*)(osgi.command.function=*))
+
+#
+# Security properties
+#
+# To enable OSGi security, uncomment the properties below,
+# install the framework-security feature and restart.
+#
+#java.security.policy=${karaf.etc}/all.policy
+#org.osgi.framework.security=osgi
+#org.osgi.framework.trust.repositories=${karaf.etc}/trustStore.ks
+
+#
+# HA/Lock configuration
+#
+# Karaf uses a lock mechanism to know which instance is the master (HA)
+# The lock can be on the filesystem (default) or on a database.
+#
+# See http://karaf.apache.org/manual/latest/users-guide/failover.html for details.
+#
+# Even using a single instance, Karaf creates the lock file
+# You can specify the location of the lock file using the
+# karaf.lock.dir=/path/to/the/directory/containing/the/lock
+#
diff --git a/dist-karaf/itest/pom.xml b/dist-karaf/itest/pom.xml
new file mode 100644
index 0000000..e9a991c
--- /dev/null
+++ b/dist-karaf/itest/pom.xml
@@ -0,0 +1,259 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    
+    <artifactId>brooklyn-itest</artifactId>
+    <name>Brooklyn Karaf pax-exam itest</name>
+
+    <parent>
+        <groupId>org.apache.brooklyn</groupId>
+        <artifactId>brooklyn-dist-karaf</artifactId>
+        <version>0.10.0-SNAPSHOT</version>  <!-- BROOKLYN_VERSION -->
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <properties>
+        <includedTestGroups />
+        <excludedTestGroups>org.apache.brooklyn.test.IntegrationTest</excludedTestGroups>
+    </properties>
+
+    <dependencies>
+        <!-- Pax Exam Dependencies -->
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-junit4</artifactId>
+            <version>${pax.exam.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-invoker-junit</artifactId>
+            <version>${pax.exam.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.12</version>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- Karaf Container -->
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-container-karaf</artifactId>
+            <version>${pax.exam.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-inject</artifactId>
+            <version>${pax.exam.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-extender-service</artifactId>
+            <version>${pax.exam.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- Preferred link because it does not require an mvn url handler implicitely. -->
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-link-mvn</artifactId>
+            <version>${pax.exam.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-link-assembly</artifactId>
+            <version>${pax.exam.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <version>${logback.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>javax.inject</groupId>
+            <artifactId>javax.inject</artifactId>
+            <version>1</version> <!-- TODO: version property? -->
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <version>${org.osgi.core.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <version>${org.osgi.compendium.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <!-- framework to test with -->
+        <dependency>
+            <groupId>org.apache.karaf.features</groupId>
+            <artifactId>standard</artifactId>
+            <version>${karaf.version}</version>
+            <type>xml</type>
+            <classifier>features</classifier>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.karaf.features</groupId>
+            <artifactId>org.apache.karaf.features.core</artifactId>
+            <version>${karaf.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.karaf.system</groupId>
+            <artifactId>org.apache.karaf.system.core</artifactId>
+            <version>${karaf.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.karaf</groupId>
+            <artifactId>apache-karaf</artifactId>
+            <version>${karaf.version}</version>
+            <type>zip</type>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.karaf.shell</groupId>
+                    <artifactId>org.apache.karaf.shell.dev</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <!-- project deps -->
+        <dependency>
+          <groupId>${project.groupId}</groupId>
+          <artifactId>brooklyn-features</artifactId>
+          <version>${project.version}</version>
+          <type>xml</type>
+          <classifier>features</classifier>
+          <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>apache-brooklyn</artifactId>
+            <version>${project.version}</version>
+            <type>zip</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.geronimo.specs</groupId>
+            <artifactId>geronimo-jta_1.1_spec</artifactId>
+            <version>${geronimo-jta_1.1_spec.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>brooklyn-rest-resources</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>brooklyn-rest-resources</artifactId>
+            <version>${project.version}</version>
+            <classifier>tests</classifier>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>brooklyn-utils-common</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.ops4j.pax.tinybundles</groupId>
+            <artifactId>tinybundles</artifactId>
+            <version>${tinybundles.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>${maven.compiler.source}</source>
+                    <target>${maven.compiler.target}</target>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.servicemix.tooling</groupId>
+                <artifactId>depends-maven-plugin</artifactId>
+                <version>1.2</version>
+                <executions>
+                    <execution>
+                        <id>generate-depends-file</id>
+                        <goals>
+                            <goal>generate-depends-file</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <groups>${includedTestGroups}</groups>
+                    <excludedGroups>${excludedTestGroups}</excludedGroups>
+                    <systemPropertyVariables>
+                        <org.ops4j.pax.url.mvn.localRepository>${settings.localRepository}</org.ops4j.pax.url.mvn.localRepository>
+                    </systemPropertyVariables>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>Integration</id>
+            <properties>
+              <includedTestGroups>org.apache.brooklyn.test.IntegrationTest</includedTestGroups>
+              <excludedTestGroups />
+            </properties>
+        </profile>
+    </profiles>
+</project>
diff --git a/dist-karaf/itest/src/test/java/org/apache/brooklyn/AssemblyTest.java b/dist-karaf/itest/src/test/java/org/apache/brooklyn/AssemblyTest.java
new file mode 100644
index 0000000..a4ccc0d
--- /dev/null
+++ b/dist-karaf/itest/src/test/java/org/apache/brooklyn/AssemblyTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.brooklyn;
+
+import static org.apache.brooklyn.KarafTestUtils.defaultOptionsWith;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import javax.inject.Inject;
+
+import org.apache.karaf.features.BootFinished;
+import org.apache.karaf.features.FeaturesService;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+import org.ops4j.pax.exam.util.Filter;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Tests the apache-brooklyn karaf runtime assembly.
+ * 
+ * Keeping it a non-integration test so we have at least a basic OSGi sanity check. (takes 14 sec)
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class AssemblyTest {
+
+    @Inject
+    private BundleContext bc;
+
+    @Inject
+    @Filter(timeout = 120000)
+    protected FeaturesService featuresService;
+
+    /**
+     * To make sure the tests run only when the boot features are fully
+     * installed
+     */
+    @Inject
+    @Filter(timeout = 120000)
+    BootFinished bootFinished;
+
+    @Configuration
+    public static Option[] configuration() throws Exception {
+        return defaultOptionsWith(
+            // Uncomment this for remote debugging the tests on port 5005
+            // KarafDistributionOption.debugConfiguration()
+        );
+    }
+
+    @Test
+    public void shouldHaveBundleContext() {
+        assertNotNull(bc);
+    }
+
+    @Test
+    public void checkEventFeature() throws Exception {
+        assertTrue(featuresService.isInstalled(featuresService.getFeature("eventadmin")));
+    }
+
+    @Test
+    public void checkBrooklynCoreFeature() throws Exception {
+        featuresService.installFeature("brooklyn-core");
+        assertTrue(featuresService.isInstalled(featuresService.getFeature("brooklyn-core")));
+    }
+
+}
diff --git a/dist-karaf/itest/src/test/java/org/apache/brooklyn/KarafTestUtils.java b/dist-karaf/itest/src/test/java/org/apache/brooklyn/KarafTestUtils.java
new file mode 100644
index 0000000..d8920ef
--- /dev/null
+++ b/dist-karaf/itest/src/test/java/org/apache/brooklyn/KarafTestUtils.java
@@ -0,0 +1,84 @@
+/*
+ * 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.brooklyn;
+
+import static org.ops4j.pax.exam.CoreOptions.junitBundles;
+import static org.ops4j.pax.exam.CoreOptions.maven;
+import static org.ops4j.pax.exam.MavenUtils.asInProject;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.configureConsole;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.editConfigurationFilePut;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.features;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.karafDistributionConfiguration;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.logLevel;
+
+import java.io.File;
+
+import org.apache.brooklyn.util.text.Identifiers;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.karaf.options.LogLevelOption.LogLevel;
+import org.ops4j.pax.exam.options.MavenArtifactUrlReference;
+import org.ops4j.pax.exam.options.MavenUrlReference;
+
+import com.google.common.collect.ObjectArrays;
+
+public class KarafTestUtils {
+    public static final Option[] DEFAULT_OPTIONS = {
+        karafDistributionConfiguration()
+            .frameworkUrl(brooklynKarafDist())
+            .unpackDirectory(new File("target/paxexam/unpack/"))
+            .useDeployFolder(false),
+        configureConsole().ignoreLocalConsole(),
+        logLevel(LogLevel.INFO),
+        features(karafStandardFeaturesRepository(), "eventadmin"),
+        junitBundles(),
+        editConfigurationFilePut("etc/org.apache.brooklyn.osgilauncher.cfg", "persistMode", "AUTO"),
+        editConfigurationFilePut("etc/org.apache.brooklyn.osgilauncher.cfg", "persistenceDir", new File("target/paxexam/persistence/" + Identifiers.makeRandomId(6) + "/").getAbsolutePath()),
+    };
+
+    public static MavenUrlReference karafStandardFeaturesRepository() {
+        return maven()
+                .groupId("org.apache.karaf.features")
+                .artifactId("standard")
+                .type("xml")
+                .classifier("features")
+                .version(asInProject());
+    }
+
+
+    public static MavenArtifactUrlReference brooklynKarafDist() {
+        return maven()
+                .groupId("org.apache.brooklyn")
+                .artifactId("apache-brooklyn")
+                .type("zip")
+                .version(asInProject());
+    }
+
+    public static Option[] defaultOptionsWith(Option... options) {
+        return ObjectArrays.concat(DEFAULT_OPTIONS, options, Option.class);
+    }
+
+    public static MavenUrlReference brooklynFeaturesRepository() {
+        return maven()
+                .groupId("org.apache.brooklyn")
+                .artifactId("brooklyn-features")
+                .type("xml")
+                .classifier("features")
+                .versionAsInProject();
+    }
+}
diff --git a/dist-karaf/itest/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogBomScannerTest.java b/dist-karaf/itest/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogBomScannerTest.java
new file mode 100644
index 0000000..39f959c
--- /dev/null
+++ b/dist-karaf/itest/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogBomScannerTest.java
@@ -0,0 +1,176 @@
+/*
+ * 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.brooklyn.core.catalog.internal;
+
+import org.apache.brooklyn.api.catalog.CatalogItem;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.core.BrooklynVersion;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.test.IntegrationTest;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.time.Duration;
+import org.apache.karaf.features.BootFinished;
+import org.apache.karaf.features.FeaturesService;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerMethod;
+import org.ops4j.pax.exam.util.Filter;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Inject;
+
+import java.util.Dictionary;
+
+import static org.apache.brooklyn.KarafTestUtils.defaultOptionsWith;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.editConfigurationFilePut;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.keepRuntimeFolder;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.logLevel;
+
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerMethod.class)
+public class CatalogBomScannerTest {
+
+    private static final Logger LOG = LoggerFactory.getLogger(CatalogBomScannerTest.class);
+    public static final String KARAF_INIT_BLUEPRINT_BOMSCANNER_PID = "org.apache.brooklyn.core.catalog.bomscanner";
+
+    @Inject
+    @Filter(timeout = 120000)
+    protected FeaturesService featuresService;
+
+    @Inject
+    @Filter(timeout = 120000)
+    protected ConfigurationAdmin configAdmin;
+
+    @Inject
+    @Filter(timeout = 120000)
+    protected ManagementContext managementContext;
+
+    /**
+     * To make sure the tests run only when the boot features are fully
+     * installed
+     */
+    @Inject
+    @Filter(timeout = 120000)
+    BootFinished bootFinished;
+
+
+    @Configuration
+    public static Option[] configuration() throws Exception {
+        return defaultOptionsWith(
+            // Uncomment this for remote debugging the tests on port 5005
+//             , KarafDistributionOption.debugConfiguration()
+        );
+    }
+
+    @Test
+    @Category(IntegrationTest.class)
+    public void shouldFindWebAppCatalogExampleOnlyAfterItsFeatureIsInstalled() throws Exception {
+
+        final CatalogItem<?, ?> catalogItem = managementContext.getCatalog()
+            .getCatalogItem("load-balancer", BrooklynVersion.get());  // from brooklyn-software-webapp
+        assertNull(catalogItem);
+
+        featuresService.installFeature("brooklyn-software-webapp", BrooklynVersion.get());
+
+        Asserts.succeedsEventually(MutableMap.of("timeout", Duration.TEN_SECONDS), new Runnable() {
+            @Override
+            public void run() {
+                final CatalogItem<?, ?> lb = managementContext.getCatalog()
+                    .getCatalogItem("load-balancer", BrooklynVersion.get());
+                assertNotNull(lb);
+            }
+        });
+    }
+
+    @Test
+    @Category(IntegrationTest.class)
+    public void shouldNotFindNoSqlCatalogExampleIfItIsBlacklisted() throws Exception {
+
+        // verify no NoSQL entities are loaded yet
+        final String riakTemplate = "bash-web-and-riak-template"; // from brooklyn-software-nosql
+        CatalogItem<?, ?> catalogItem = getCatalogItem(riakTemplate);
+        assertNull(catalogItem);
+
+        final String redisStore = "org.apache.brooklyn.entity.nosql.redis.RedisStore"; // ditto
+        catalogItem = getCatalogItem(redisStore);
+        assertNull(catalogItem);
+
+        // blacklist the org.apache.brooklyn.software-nosql bundle
+        final org.osgi.service.cm.Configuration bomScannerConfig =
+            configAdmin.getConfiguration(KARAF_INIT_BLUEPRINT_BOMSCANNER_PID);
+        final Dictionary<String, Object> bomProps = bomScannerConfig.getProperties();
+        assertEquals(".*", bomProps.get("whiteList"));
+        assertEquals("", bomProps.get("blackList"));
+
+        bomProps.put("blackList", ".*nosql.*");
+        bomScannerConfig.update(bomProps);
+
+        // install the NoSQL feature
+        featuresService.installFeature("brooklyn-software-nosql", BrooklynVersion.get());
+
+        // verify that the non-template entity org.apache.brooklyn.entity.nosql.redis.RedisStore gets added to catalog
+        verifyCatalogItemEventually(redisStore, true);
+
+        // verify that the template application hasn't made it into the catalog (because it's blacklisted)
+        catalogItem = getCatalogItem(riakTemplate);
+        assertNull(catalogItem);
+
+        // For completeness let's uninstall the bundle, un-blacklist nosql, and install again
+        featuresService.uninstallFeature("brooklyn-software-nosql", BrooklynVersion.get());
+
+        // verify it's gone away
+        verifyCatalogItemEventually(redisStore, false);
+
+        // un-blacklist nosql
+        bomProps.put("blackList", "");
+        bomScannerConfig.update(bomProps);
+
+        // install it again
+        featuresService.installFeature("brooklyn-software-nosql", BrooklynVersion.get());
+
+        // now the application should make it into the catalog
+        verifyCatalogItemEventually(redisStore, true);
+
+    }
+
+    private void verifyCatalogItemEventually(final String redisStore, final boolean isItThere) {
+        Asserts.succeedsEventually(MutableMap.of("timeout", Duration.TEN_SECONDS), new Runnable() {
+            @Override
+            public void run() {
+                final CatalogItem<?, ?> redis = getCatalogItem(redisStore);
+                assertEquals(null != redis, isItThere);
+            }
+        });
+    }
+
+    private CatalogItem<?, ?> getCatalogItem(String itemName) {
+        return managementContext.getCatalog().getCatalogItem(itemName, BrooklynVersion.get());
+    }
+
+}
diff --git a/dist-karaf/itest/src/test/java/org/apache/brooklyn/core/catalog/internal/DefaultBomLoadTest.java b/dist-karaf/itest/src/test/java/org/apache/brooklyn/core/catalog/internal/DefaultBomLoadTest.java
new file mode 100644
index 0000000..31d500e
--- /dev/null
+++ b/dist-karaf/itest/src/test/java/org/apache/brooklyn/core/catalog/internal/DefaultBomLoadTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.brooklyn.core.catalog.internal;
+
+
+import org.apache.brooklyn.api.catalog.CatalogItem;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.core.BrooklynVersion;
+import org.apache.brooklyn.test.IntegrationTest;
+import org.apache.karaf.features.BootFinished;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerMethod;
+import org.ops4j.pax.exam.util.Filter;
+
+import javax.inject.Inject;
+
+import static org.apache.brooklyn.KarafTestUtils.defaultOptionsWith;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerMethod.class)
+@Category(IntegrationTest.class)
+public class DefaultBomLoadTest {
+
+    /**
+     * To make sure the tests run only when the boot features are fully installed
+     */
+    @Inject
+    @Filter(timeout = 120000)
+    BootFinished bootFinished;
+
+    @Inject
+    @Filter(timeout = 120000)
+    protected ManagementContext managementContext;
+
+
+    @Configuration
+    public static Option[] configuration() throws Exception {
+        return defaultOptionsWith(
+            // Uncomment this for remote debugging the tests on port 5005
+//             , KarafDistributionOption.debugConfiguration()
+        );
+    }
+
+
+    @Test
+    @Category(IntegrationTest.class)
+    public void shouldHaveLoadedDefaultCatalogBom() throws Exception {
+        final CatalogItem<?, ?> catalogItem = managementContext.getCatalog()
+            .getCatalogItem("server-template", BrooklynVersion.get());  // from brooklyn-software-base catalog.bom
+        assertNotNull(catalogItem);
+        assertEquals("Template: Server", catalogItem.getDisplayName());
+    }
+}
diff --git a/dist-karaf/itest/src/test/java/org/apache/brooklyn/launcher/osgi/OsgiLauncherTest.java b/dist-karaf/itest/src/test/java/org/apache/brooklyn/launcher/osgi/OsgiLauncherTest.java
new file mode 100644
index 0000000..b825112
--- /dev/null
+++ b/dist-karaf/itest/src/test/java/org/apache/brooklyn/launcher/osgi/OsgiLauncherTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.brooklyn.launcher.osgi;
+
+import static org.apache.brooklyn.KarafTestUtils.defaultOptionsWith;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.editConfigurationFilePut;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.features;
+
+import java.io.IOException;
+
+import javax.inject.Inject;
+
+import org.apache.brooklyn.KarafTestUtils;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.test.IntegrationTest;
+import org.apache.karaf.features.BootFinished;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+import org.ops4j.pax.exam.util.Filter;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+@Category(IntegrationTest.class)
+public class OsgiLauncherTest {
+
+    private static final String TEST_VALUE_RUNTIME = "test.value";
+    private static final String TEST_KEY_RUNTIME = "test.key";
+    private static final String TEST_VALUE_IN_CFG = "test.cfg";
+    private static final String TEST_KEY_IN_CFG = "test.key.in.cfg";
+
+    @Inject
+    @Filter(timeout = 120000)
+    protected ManagementContext mgmt;
+
+    @Inject
+    @Filter(timeout = 120000)
+    protected ConfigurationAdmin configAdmin;
+
+    /**
+     * To make sure the tests run only when the boot features are fully
+     * installed
+     */
+    @Inject
+    @Filter(timeout = 120000)
+    BootFinished bootFinished;
+
+    @Configuration
+    public static Option[] configuration() throws Exception {
+        return defaultOptionsWith(
+                editConfigurationFilePut("etc/org.apache.brooklyn.osgilauncher.cfg", "globalBrooklynPropertiesFile", ""),
+                editConfigurationFilePut("etc/org.apache.brooklyn.osgilauncher.cfg", "localBrooklynPropertiesFile", ""),
+                editConfigurationFilePut("etc/brooklyn.cfg", TEST_KEY_IN_CFG, TEST_VALUE_IN_CFG),
+                features(KarafTestUtils.brooklynFeaturesRepository(), "brooklyn-osgi-launcher")
+                // Uncomment this for remote debugging the tests on port 5005
+                // ,KarafDistributionOption.debugConfiguration()
+        );
+    }
+
+    @Test
+    public void testConfig() throws IOException {
+        assertFalse(mgmt.getConfig().getAllConfig().containsKey(TEST_KEY_RUNTIME));
+        org.osgi.service.cm.Configuration config = configAdmin.getConfiguration("brooklyn", null);
+        assertEquals(config.getProperties().get(TEST_KEY_IN_CFG), TEST_VALUE_IN_CFG);
+        config.getProperties().put(TEST_KEY_RUNTIME, TEST_VALUE_RUNTIME);
+        config.update();
+        Asserts.succeedsEventually(new Runnable() {
+            @Override
+            public void run() {
+                assertEquals(TEST_VALUE_RUNTIME, mgmt.getConfig().getFirst(TEST_KEY_RUNTIME));
+            }
+        });
+    }
+}
diff --git a/dist-karaf/itest/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncherTest.java b/dist-karaf/itest/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncherTest.java
new file mode 100644
index 0000000..f182f98
--- /dev/null
+++ b/dist-karaf/itest/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncherTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.brooklyn.rest;
+
+import static org.apache.brooklyn.KarafTestUtils.defaultOptionsWith;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.editConfigurationFilePut;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.features;
+
+import java.util.concurrent.Callable;
+
+import org.apache.brooklyn.KarafTestUtils;
+import org.apache.brooklyn.entity.brooklynnode.BrooklynNode;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.http.HttpAsserts;
+import org.apache.brooklyn.util.http.HttpTool;
+import org.apache.http.HttpStatus;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+@Ignore // TODO: re-enable after brooklyn is properly initialized within the OSGI environment
+public class BrooklynRestApiLauncherTest {
+
+    private static final String HTTP_PORT = "9998";
+    private static final String ROOT_URL = "http://localhost:" + HTTP_PORT;
+
+    @Configuration
+    public static Option[] configuration() throws Exception {
+        return defaultOptionsWith(
+            editConfigurationFilePut("etc/org.ops4j.pax.web.cfg", "org.osgi.service.http.port", HTTP_PORT),
+            features(KarafTestUtils.brooklynFeaturesRepository(), "brooklyn-software-base")
+            // Uncomment this for remote debugging the tests on port 5005
+            // ,KarafDistributionOption.debugConfiguration()
+        );
+    }
+
+    @Test
+    public void testStart() throws Exception {
+        ensureBrooklynStarted();
+
+        final String testUrl = ROOT_URL + "/v1/catalog/entities";
+        int code = Asserts.succeedsEventually(new Callable<Integer>() {
+            @Override
+            public Integer call() throws Exception {
+                int code = HttpTool.getHttpStatusCode(testUrl);
+                if (code == HttpStatus.SC_FORBIDDEN) {
+                    throw new RuntimeException("Retry request");
+                } else {
+                    return code;
+                }
+            }
+        });
+        HttpAsserts.assertHealthyStatusCode(code);
+        HttpAsserts.assertContentContainsText(testUrl, BrooklynNode.class.getSimpleName());
+    }
+
+    private void ensureBrooklynStarted() {
+        final String upUrl = ROOT_URL + "/v1/server/up";
+        HttpAsserts.assertContentEventuallyMatches(upUrl, "true");
+    }
+}
diff --git a/dist-karaf/itest/src/test/java/org/apache/brooklyn/security/CustomSecurityProvider.java b/dist-karaf/itest/src/test/java/org/apache/brooklyn/security/CustomSecurityProvider.java
new file mode 100644
index 0000000..ca9ac0e
--- /dev/null
+++ b/dist-karaf/itest/src/test/java/org/apache/brooklyn/security/CustomSecurityProvider.java
@@ -0,0 +1,33 @@
+/*
+ * 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.brooklyn.security;
+
+import javax.servlet.http.HttpSession;
+
+import org.apache.brooklyn.rest.security.provider.AbstractSecurityProvider;
+import org.apache.brooklyn.rest.security.provider.SecurityProvider;
+
+public class CustomSecurityProvider extends AbstractSecurityProvider implements SecurityProvider {
+
+    @Override
+    public boolean authenticate(HttpSession session, String user, String password) {
+        return "custom".equals(user);
+    }
+
+}
diff --git a/dist-karaf/itest/src/test/java/org/apache/brooklyn/security/CustomSecurityProviderTest.java b/dist-karaf/itest/src/test/java/org/apache/brooklyn/security/CustomSecurityProviderTest.java
new file mode 100644
index 0000000..3856c80
--- /dev/null
+++ b/dist-karaf/itest/src/test/java/org/apache/brooklyn/security/CustomSecurityProviderTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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.brooklyn.security;
+
+import static org.apache.brooklyn.KarafTestUtils.defaultOptionsWith;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertEquals;
+import static org.ops4j.pax.exam.CoreOptions.streamBundle;
+
+import java.io.IOException;
+
+import javax.inject.Inject;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.FailedLoginException;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.core.internal.BrooklynProperties;
+import org.apache.brooklyn.rest.BrooklynWebConfig;
+import org.apache.brooklyn.rest.security.jaas.BrooklynLoginModule;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.test.IntegrationTest;
+import org.apache.karaf.features.BootFinished;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+import org.ops4j.pax.exam.util.Filter;
+import org.ops4j.pax.tinybundles.core.TinyBundles;
+import org.osgi.framework.Constants;
+
+import com.google.common.collect.ImmutableSet;
+
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+@Category(IntegrationTest.class)
+public class CustomSecurityProviderTest {
+    private static final String WEBCONSOLE_REALM = "webconsole";
+
+    /**
+     * To make sure the tests run only when the boot features are fully
+     * installed
+     */
+    @Inject
+    @Filter(timeout = 120000)
+    BootFinished bootFinished;
+    
+    @Inject
+    @Filter(timeout = 120000)
+    ManagementContext managementContext;
+
+    @Configuration
+    public static Option[] configuration() throws Exception {
+        return defaultOptionsWith(
+            streamBundle(TinyBundles.bundle()
+                .add(CustomSecurityProvider.class)
+                .add("OSGI-INF/blueprint/security.xml", CustomSecurityProviderTest.class.getResource("/custom-security-bp.xml"))
+                .set(Constants.BUNDLE_MANIFESTVERSION, "2") // defaults to 1 which doesn't work
+                .set(Constants.BUNDLE_SYMBOLICNAME, "org.apache.brooklyn.test.security")
+                .set(Constants.BUNDLE_VERSION, "1.0.0")
+                .set(Constants.DYNAMICIMPORT_PACKAGE, "*")
+                .set(Constants.EXPORT_PACKAGE, CustomSecurityProvider.class.getPackage().getName())
+                .build())
+            // Uncomment this for remote debugging the tests on port 5005
+            // ,KarafDistributionOption.debugConfiguration()
+        );
+    }
+
+    @Before
+    public void setUp() {
+        // Works only before initializing the security provider (i.e. before first use)
+        // TODO Dirty hack to inject the needed properties. Improve once managementContext is configurable.
+        // Alternatively re-register a test managementContext service (how?)
+        BrooklynProperties brooklynProperties = (BrooklynProperties)managementContext.getConfig();
+        brooklynProperties.put(BrooklynWebConfig.SECURITY_PROVIDER_CLASSNAME.getName(), CustomSecurityProvider.class.getCanonicalName());
+    }
+
+    @Test(expected = FailedLoginException.class)
+    public void checkLoginFails() throws LoginException {
+        assertRealmRegisteredEventually(WEBCONSOLE_REALM);
+        doLogin("invalid", "auth");
+    }
+
+    @Test
+    public void checkLoginSucceeds() throws LoginException {
+        assertRealmRegisteredEventually(WEBCONSOLE_REALM);
+        String user = "custom";
+        LoginContext lc = doLogin(user, "password");
+        Subject subject = lc.getSubject();
+        assertNotNull(subject);
+        assertEquals(subject.getPrincipals(), ImmutableSet.of(
+                new BrooklynLoginModule.UserPrincipal(user),
+                new BrooklynLoginModule.RolePrincipal("users")));
+    }
+
+    private LoginContext doLogin(final String username, final String password) throws LoginException {
+        assertRealmRegisteredEventually(WEBCONSOLE_REALM);
+        LoginContext lc = new LoginContext(WEBCONSOLE_REALM, new CallbackHandler() {
+            public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+                for (int i = 0; i < callbacks.length; i++) {
+                    Callback callback = callbacks[i];
+                    if (callback instanceof PasswordCallback) {
+                        PasswordCallback passwordCallback = (PasswordCallback)callback;
+                        passwordCallback.setPassword(password.toCharArray());
+                    } else if (callback instanceof NameCallback) {
+                        NameCallback nameCallback = (NameCallback)callback;
+                        nameCallback.setName(username);
+                    }
+                }
+            }
+        });
+        lc.login();
+        return lc;
+    }
+
+    private void assertRealmRegisteredEventually(final String userPassRealm) {
+        // Need to wait a bit for the realm to get registered, any OSGi way to do this?
+        Asserts.succeedsEventually(new Runnable() {
+            @Override
+            public void run() {
+                javax.security.auth.login.Configuration initialConfig = javax.security.auth.login.Configuration.getConfiguration();
+                AppConfigurationEntry[] realm = initialConfig.getAppConfigurationEntry(userPassRealm);
+                assertNotNull(realm);
+            }
+        });
+    }
+
+}
diff --git a/dist-karaf/itest/src/test/java/org/apache/brooklyn/security/StockSecurityProviderTest.java b/dist-karaf/itest/src/test/java/org/apache/brooklyn/security/StockSecurityProviderTest.java
new file mode 100644
index 0000000..8ef5ceb
--- /dev/null
+++ b/dist-karaf/itest/src/test/java/org/apache/brooklyn/security/StockSecurityProviderTest.java
@@ -0,0 +1,191 @@
+/*
+ * 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.brooklyn.security;
+
+import static org.apache.brooklyn.KarafTestUtils.defaultOptionsWith;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.IOException;
+import java.util.concurrent.Callable;
+
+import javax.inject.Inject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.FailedLoginException;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.core.internal.BrooklynProperties;
+import org.apache.brooklyn.rest.BrooklynWebConfig;
+import org.apache.brooklyn.rest.security.provider.ExplicitUsersSecurityProvider;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.test.IntegrationTest;
+import org.apache.http.HttpStatus;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.karaf.features.BootFinished;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+import org.ops4j.pax.exam.util.Filter;
+
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+@Category(IntegrationTest.class)
+public class StockSecurityProviderTest {
+
+    private static final String WEBCONSOLE_REALM = "webconsole";
+    private static final String USER = "admin";
+    private static final String PASSWORD = "password";
+
+    /**
+     * To make sure the tests run only when the boot features are fully
+     * installed
+     */
+    @Inject
+    @Filter(timeout = 120000)
+    BootFinished bootFinished;
+    
+    @Inject
+    @Filter(timeout = 120000)
+    ManagementContext managementContext;
+
+    @Configuration
+    public static Option[] configuration() throws Exception {
+        return defaultOptionsWith(
+            // Uncomment this for remote debugging the tests on port 5005
+            // KarafDistributionOption.debugConfiguration()
+        );
+    }
+
+    @Before
+    public void setUp() {
+        //Works only before initializing the security provider (i.e. before first use)
+        addUser(USER, PASSWORD);
+    }
+
+    @Test(expected = FailedLoginException.class)
+    public void checkLoginFails() throws LoginException {
+        doLogin("invalid", "auth");
+    }
+
+    @Test
+    public void checkLoginSucceeds() throws LoginException {
+        LoginContext lc = doLogin(USER, PASSWORD);
+        assertNotNull(lc.getSubject());
+    }
+
+    @Test
+    public void checkRestSecurityFails() throws IOException {
+        checkRestSecurity(null, null, HttpStatus.SC_UNAUTHORIZED);
+    }
+
+    @Test
+    public void checkRestSecuritySucceeds() throws IOException {
+        checkRestSecurity(USER, PASSWORD, HttpStatus.SC_OK);
+    }
+
+    private void checkRestSecurity(String username, String password, final int code) throws IOException {
+        CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
+        if (username != null && password != null) {
+            credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
+        }
+        try(CloseableHttpClient client =
+            HttpClientBuilder.create().setDefaultCredentialsProvider(credentialsProvider).build()) {
+            Asserts.succeedsEventually(new Callable<Void>() {
+                @Override
+                public Void call() throws Exception {
+                    assertResponseEquals(client, code);
+                    return null;
+                }
+            });
+        }
+    }
+
+    private void assertResponseEquals(CloseableHttpClient httpclient, int code) throws IOException, ClientProtocolException {
+        // TODO get this dynamically (from CXF service?)
+        // TODO port is static, should make it dynamic
+        HttpGet httpGet = new HttpGet("http://localhost:8081/v1/server/ha/state");
+        try (CloseableHttpResponse response = httpclient.execute(httpGet)) {
+            assertEquals(code, response.getStatusLine().getStatusCode());
+        }
+    }
+    
+
+    private void addUser(String username, String password) {
+        // TODO Dirty hack to inject the needed properties. Improve once managementContext is configurable.
+        // Alternatively re-register a test managementContext service (how?)
+        BrooklynProperties brooklynProperties = (BrooklynProperties)managementContext.getConfig();
+        brooklynProperties.put(BrooklynWebConfig.SECURITY_PROVIDER_CLASSNAME.getName(), ExplicitUsersSecurityProvider.class.getCanonicalName());
+        brooklynProperties.put(BrooklynWebConfig.USERS.getName(), username);
+        brooklynProperties.put(BrooklynWebConfig.PASSWORD_FOR_USER(username), password);
+    }
+
+    private LoginContext doLogin(final String username, final String password) throws LoginException {
+        assertRealmRegisteredEventually(WEBCONSOLE_REALM);
+        LoginContext lc = new LoginContext(WEBCONSOLE_REALM, new CallbackHandler() {
+            public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+                for (int i = 0; i < callbacks.length; i++) {
+                    Callback callback = callbacks[i];
+                    if (callback instanceof PasswordCallback) {
+                        PasswordCallback passwordCallback = (PasswordCallback)callback;
+                        passwordCallback.setPassword(password.toCharArray());
+                    } else if (callback instanceof NameCallback) {
+                        NameCallback nameCallback = (NameCallback)callback;
+                        nameCallback.setName(username);
+                    }
+                }
+            }
+        });
+        lc.login();
+        return lc;
+    }
+
+    private void assertRealmRegisteredEventually(final String userPassRealm) {
+        // Need to wait a bit for the realm to get registered, any OSGi way to do this?
+        Asserts.succeedsEventually(new Runnable() {
+            @Override
+            public void run() {
+                javax.security.auth.login.Configuration initialConfig = javax.security.auth.login.Configuration.getConfiguration();
+                AppConfigurationEntry[] realm = initialConfig.getAppConfigurationEntry(userPassRealm);
+                assertNotNull(realm);
+            }
+        });
+    }
+
+}
diff --git a/dist-karaf/itest/src/test/java/org/apache/brooklyn/test/IntegrationTest.java b/dist-karaf/itest/src/test/java/org/apache/brooklyn/test/IntegrationTest.java
new file mode 100644
index 0000000..9f5fb43
--- /dev/null
+++ b/dist-karaf/itest/src/test/java/org/apache/brooklyn/test/IntegrationTest.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.brooklyn.test;
+
+/**
+ * Used by junit for grouping tests
+ */
+public class IntegrationTest {
+
+}
diff --git a/dist-karaf/itest/src/test/resources/custom-security-bp.xml b/dist-karaf/itest/src/test/resources/custom-security-bp.xml
new file mode 100644
index 0000000..ace4454
--- /dev/null
+++ b/dist-karaf/itest/src/test/resources/custom-security-bp.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright 2015 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.
+-->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+           xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.0.0"
+           xmlns:jaxws="http://cxf.apache.org/blueprint/jaxws"
+           xmlns:jaxrs="http://cxf.apache.org/blueprint/jaxrs"
+           xmlns:cxf="http://cxf.apache.org/blueprint/core"
+           xmlns:jaas="http://karaf.apache.org/xmlns/jaas/v1.0.0"
+           xsi:schemaLocation="
+             http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
+             http://cxf.apache.org/blueprint/jaxws http://cxf.apache.org/schemas/blueprint/jaxws.xsd
+             http://cxf.apache.org/blueprint/jaxrs http://cxf.apache.org/schemas/blueprint/jaxrs.xsd
+             http://cxf.apache.org/blueprint/core http://cxf.apache.org/schemas/blueprint/core.xsd
+             http://karaf.apache.org/xmlns/jaas/v1.0.0 http://karaf.apache.org/xmlns/jaas/v1.0.0
+             ">
+
+    <jaas:config name="webconsole" rank="1">
+        <jaas:module className="org.apache.brooklyn.rest.security.jaas.BrooklynLoginModule"
+                     flags="required">
+            brooklyn.webconsole.security.provider.symbolicName=org.apache.brooklyn.test.security
+            brooklyn.webconsole.security.provider.version=1.0.0
+            brooklyn.webconsole.security.provider.role=users
+        </jaas:module>
+    </jaas:config>
+</blueprint>
diff --git a/dist-karaf/itest/src/test/resources/exam.properties b/dist-karaf/itest/src/test/resources/exam.properties
new file mode 100644
index 0000000..d516df6
--- /dev/null
+++ b/dist-karaf/itest/src/test/resources/exam.properties
@@ -0,0 +1,21 @@
+################################################################################
+#
+#    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.
+#
+################################################################################
+
+pax.exam.logging = none
+pax.exam.service.timeout = 5000
diff --git a/dist-karaf/itest/src/test/resources/logback.xml b/dist-karaf/itest/src/test/resources/logback.xml
new file mode 100644
index 0000000..7c08bb7
--- /dev/null
+++ b/dist-karaf/itest/src/test/resources/logback.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<configuration>
+
+  <!--  log to System.out on console  -->
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
+        </encoder>
+    </appender>
+  
+  <!--  log to file test.log  -->
+    <appender name="TEST_LOG" class="ch.qos.logback.core.FileAppender">
+        <file>test.log</file>
+        <encoder>
+            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
+        </encoder>
+    </appender>
+
+    <root level="INFO">
+        <appender-ref ref="STDOUT" />
+        <appender-ref ref="TEST_LOG" />
+    </root>
+    <logger name="org.ops4j.pax.exam" level="INFO" />
+
+</configuration>
\ No newline at end of file
diff --git a/dist-karaf/pom.xml b/dist-karaf/pom.xml
new file mode 100644
index 0000000..c96fe2a
--- /dev/null
+++ b/dist-karaf/pom.xml
@@ -0,0 +1,185 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>pom</packaging>
+
+    <artifactId>brooklyn-dist-karaf</artifactId>
+
+    <name>Brooklyn Karaf Distribution</name>
+    <description>
+        Brooklyn Apache Karaf distribution archive
+    </description>
+
+    <parent>
+        <groupId>org.apache.brooklyn</groupId>
+        <!-- if this depends (via inheritance from brooklyn-dist) on brooklyn-server/parent, the usual parent point,
+             then karaf-itest has failures -->
+        <artifactId>brooklyn-server</artifactId>
+        <version>0.10.0-SNAPSHOT</version>  <!-- BROOKLYN_VERSION -->
+        <relativePath>../../brooklyn-server/pom.xml</relativePath>
+    </parent>
+
+    <properties>
+        <!-- TODO: duplicates in brooklyn-server/karaf/pom.xml -->
+        <karaf.version>4.0.4</karaf.version>
+        <logback.version>1.0.7</logback.version>
+        <org.osgi.core.version>6.0.0</org.osgi.core.version>
+        <geronimo-jta_1.1_spec.version>1.1.1</geronimo-jta_1.1_spec.version>
+
+        <org.osgi.compendium.version>5.0.0</org.osgi.compendium.version>
+        <lifecycle-mapping-plugin.version>1.0.0</lifecycle-mapping-plugin.version>
+
+        <!-- pax-exam -->
+        <pax.exam.version>4.7.0</pax.exam.version>
+        <pax.url.version>2.4.3</pax.url.version>
+        <ops4j.base.version>1.5.0</ops4j.base.version>
+        <tinybundles.version>1.0.0</tinybundles.version>
+
+        <!-- feature repositories -->
+        <servicemix.version>6.0.0</servicemix.version>
+        <reflections.bundle.version>0.9.9_1</reflections.bundle.version> <!-- see reflections.version -->
+
+        <maven.compiler.source>${java.version}</maven.compiler.source>
+        <maven.compiler.target>${java.version}</maven.compiler.target>
+    </properties>
+
+    <modules>
+        <module>apache-brooklyn</module>
+        <module>itest</module>
+    </modules>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.ops4j.base</groupId>
+                <artifactId>ops4j-base-lang</artifactId>
+                <version>${ops4j.base.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.ops4j.base</groupId>
+                <artifactId>ops4j-base-util-property</artifactId>
+                <version>${ops4j.base.version}</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.eclipse.m2e</groupId>
+                    <artifactId>lifecycle-mapping</artifactId>
+                    <version>${lifecycle-mapping-plugin.version}</version>
+                    <configuration>
+                        <lifecycleMappingMetadata>
+                            <pluginExecutions>
+                                <pluginExecution>
+                                    <pluginExecutionFilter>
+                                        <groupId>org.apache.maven.plugins</groupId>
+                                        <artifactId>maven-enforcer-plugin</artifactId>
+                                        <versionRange>[0,)</versionRange>
+                                        <goals>
+                                            <goal>enforce</goal>
+                                        </goals>
+                                    </pluginExecutionFilter>
+                                    <action>
+                                        <ignore/>
+                                    </action>
+                                </pluginExecution>
+                                <pluginExecution>
+                                    <pluginExecutionFilter>
+                                        <groupId>org.codehaus.mojo</groupId>
+                                        <artifactId>build-helper-maven-plugin</artifactId>
+                                        <versionRange>[0,)</versionRange>
+                                        <goals>
+                                            <goal>attach-artifact</goal>
+                                        </goals>
+                                    </pluginExecutionFilter>
+                                    <action>
+                                        <ignore/>
+                                    </action>
+                                </pluginExecution>
+                                <pluginExecution>
+                                    <pluginExecutionFilter>
+                                        <groupId>org.apache.karaf.tooling</groupId>
+                                        <artifactId>karaf-maven-plugin</artifactId>
+                                        <versionRange>[0,)</versionRange>
+                                        <goals>
+                                            <goal>assembly</goal>
+                                            <goal>commands-generate-help</goal>
+                                            <goal>features-generate-descriptor</goal>
+                                        </goals>
+                                    </pluginExecutionFilter>
+                                    <action>
+                                        <ignore/>
+                                    </action>
+                                </pluginExecution>
+                                <pluginExecution>
+                                    <pluginExecutionFilter>
+                                        <groupId>org.apache.servicemix.tooling</groupId>
+                                        <artifactId>depends-maven-plugin</artifactId>
+                                        <versionRange>[0,)</versionRange>
+                                        <goals>
+                                            <goal>generate-depends-file</goal>
+                                        </goals>
+                                    </pluginExecutionFilter>
+                                    <action>
+                                        <ignore/>
+                                    </action>
+                                </pluginExecution>
+                            </pluginExecutions>
+                        </lifecycleMappingMetadata>
+                    </configuration>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.rat</groupId>
+                    <artifactId>apache-rat-plugin</artifactId>
+                    <configuration>
+                        <excludes combine.children="append">
+                            <!-- Exclude sandbox because not part of distribution: not in tgz, and not uploaded to maven-central -->
+                            <exclude>sandbox/**</exclude>
+                            <!-- Exclude release because not part of distribution: not in tgz, and not uploaded to maven-central -->
+                            <exclude>release/**</exclude>
+                            <exclude>README.md</exclude>
+                            <!-- Exclude netbeans config files (not part of the project, but often on users' drives -->
+                            <exclude>**/nbactions.xml</exclude>
+                            <exclude>**/nb-configuration.xml</exclude>
+                        </excludes>
+                    </configuration>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
+
+    <repositories>
+        <repository>
+            <id>servicemix</id>
+            <name>Apache ServiceMix Repository</name>
+            <url>http://svn.apache.org/repos/asf/servicemix/m2-repo</url>
+            <releases>
+                <enabled>true</enabled>
+            </releases>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+        </repository>
+    </repositories>
+</project>
diff --git a/pom.xml b/pom.xml
index 74b5564..01073f3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -119,6 +119,7 @@
         <module>downstream-parent</module>
         <module>all</module>
         <module>dist</module>
+        <module>dist-karaf</module>
         <module>vagrant</module>
         <module>archetypes/quickstart</module>
         <module>shared-packaging</module>