Resurrect dead 1.0 branch so code can be reused for creating a Geronimo kernel bridge


git-svn-id: https://svn.apache.org/repos/asf/geronimo/xbean/branches/xbean-1.0@417577 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/kernel/converting-gbean.apt b/kernel/converting-gbean.apt
new file mode 100644
index 0000000..5b7c683
--- /dev/null
+++ b/kernel/converting-gbean.apt
@@ -0,0 +1,54 @@
+ -----
+ Converting gbean.org from m1 to m2
+ -----
+ Jason van Zyl
+ -----
+ 
+ What follows is a little description of the the process that I go through
+ when converting an m1 project to an m2 project. In this example I'm going to
+ use gbean.org, Dain Sundstrom's [yada yada yada] ...
+ 
+ Usually when I start looking at converting a project from m1 to m2 I take a
+ quick look at the directory structure because the standard layout we
+ decided upon for m2 is slightly different from layout most common in m1 
+ and it's understandable that folks might not want to change their layout. 
+ I talked it over with Dain and because SVN makes it so easy to shuffle things
+ around he decided it would be ok to use the m2 standard. Using the m2
+ standards does make things easier because the standards for m2 are
+ encapsulated in the Super POM that all m2 project inherit from. You can sort of 
+ think of the Super POM in Maven as the analog of <<<java.lang.Object>>> in Java.
+ 
+ - pitfalls in using the default structure
+ 
+ - resources in the same directories as sources
+ 
+ - some things we don't have in m2 right now are a clover plugin
+
+ Updating the POM
+ 
+ - change pomVersion to modelVersion and 3 to 4.0.0
+ 
+ - the id element is deprecated an calculated in m2, so i changed the
+   id element to artifactId
+   
+ - currentVersion to version
+ 
+ - change the version from 1.0-SNAPSHOT to 1.0-alpha-1-SNAPSHOT
+ 
+ - package element no longer exists, it will be the job of plugins
+ 
+ - remove the siteAddress and siteDirectory elemement and replace it
+   with the distributionManagement element
+   
+ - removed the entire build element because we are using m2 standards
+   which are encapsulated in the Super POM.
+   
+ - remove the reports for now as we don't have many
+
+Annoyances
+
+ The single biggest annoyance with converting from m1 to m2 is the fact that
+ often times people have not made POMs for artifacts we have entered into the
+ repository. With m2 the build will not work if artifacts do not have the
+ accompanying POMs.
+ 
diff --git a/kernel/m2-readme.txt b/kernel/m2-readme.txt
new file mode 100644
index 0000000..3750464
--- /dev/null
+++ b/kernel/m2-readme.txt
@@ -0,0 +1,9 @@
+Trying Maven 2.x
+----------------
+
+If you want to try m2 then simply run the mavenizer.sh script which
+will produce an "m2" directory. Move into the "m2" directory and 
+
+m2 install
+
+That should compile, test, package and install
diff --git a/kernel/maven.xml b/kernel/maven.xml
new file mode 100644
index 0000000..3e2c28a
--- /dev/null
+++ b/kernel/maven.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+* Copyright 2005 the original author or authors.
+*
+*  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.
+-->
+<project default="default"
+    xmlns:j="jelly:core"
+    xmlns:ant="jelly:ant"
+    >
+
+    <!-- ==================== -->
+    <!-- Default Global Goals -->
+    <!-- ==================== -->
+
+    <goal name="default">
+        <attainGoal name="jar:install"/>
+    </goal>
+
+    <goal name="build">
+        <attainGoal name="default"/>
+    </goal>
+
+    <goal name="rebuild">
+        <attainGoal name="clean"/>
+        <attainGoal name="build"/>
+    </goal>
+
+    <!-- Check if the tests need to run -->
+    <preGoal name="test:test">
+        <j:if test="${context.getVariable('maven.test.force') == null}">
+            <j:if test="${cloveroverride != 'true'}">
+                <j:set var="uptodatePropName" value="tests.uptodate"/>
+                <j:remove var="${uptodatePropName}"/>
+                <ant:mkdir dir="${basedir}/target/test-reports/"/>
+                <j:set var="uptodateFile" value="${basedir}/target/test-reports/tstamp"/>
+
+                <ant:uptodate property="${uptodatePropName}" targetfile="${uptodateFile}">
+                    <ant:srcfiles dir="${basedir}/src/" includes="**/*"/>
+                </ant:uptodate>
+
+                <j:if test="${context.getVariable(uptodatePropName) == 'true'}">
+                    <ant:echo>NOTICE: Skipping tests; they seem to have passed already</ant:echo>
+                    <j:set var="maven.test.skip" value="true"/>
+                    <j:set var="unitTestSourcesPresent" value="false"/>
+                </j:if>
+            </j:if>
+        </j:if>
+    </preGoal>
+
+    <!-- Update the timestamp of the last successful test -->
+    <postGoal name="test:test">
+        <j:if test="${context.getVariable('maven.test.failure') == null}">
+            <ant:touch file="${basedir}/target/test-reports/tstamp"/>
+        </j:if>
+    </postGoal>
+</project>
diff --git a/kernel/project.properties b/kernel/project.properties
new file mode 100644
index 0000000..8303fb3
--- /dev/null
+++ b/kernel/project.properties
@@ -0,0 +1,34 @@
+###
+# Copyright 2005 the original author or authors.
+#
+#  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.
+###
+maven.repo.remote=http://www.gbean.org/maven,http://www.openejb.org/maven,http://www.ibiblio.org/maven
+
+maven.compile.source=1.4
+maven.compile.target=1.4
+maven.compile.deprecation=false
+maven.compile.debug=true
+maven.compile.optimize=true
+
+maven.remote.group=gbean
+maven.username=${user.name}
+maven.repo.central=beaver.codehaus.org
+maven.repo.central.directory=/dist
+
+maven.javadoc.source=1.4
+maven.javadoc.links=http://java.sun.com/j2se/1.4.1/docs/api/
+maven.javadoc.additionalparam=-linksource
+
+maven.site.deploy.method=rsync
+maven.site.deploy.clean=true
diff --git a/kernel/project.xml b/kernel/project.xml
new file mode 100644
index 0000000..2ce1f5d
--- /dev/null
+++ b/kernel/project.xml
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+* Copyright 2005 the original author or authors.
+*
+*  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.
+-->
+<project>
+    <!-- the version of maven's project object model -->
+    <pomVersion>3</pomVersion>
+
+    <name>GBean :: Kernel</name>
+    <id>gbean-kernel</id>
+    <groupId>gbean</groupId>
+    <currentVersion>1.0-SNAPSHOT</currentVersion>
+    <organization>
+        <name>GBean.org</name>
+        <url>http://gbean.org</url>
+    </organization>
+
+    <package>org.gbean</package>
+
+    <shortDescription>GBean: kernel for containers</shortDescription>
+
+    <description>
+        GBean is a kernel architecture and server for ioc containers such as spring.
+    </description>
+
+    <url>http://www.gbean.org/</url>
+
+    <siteAddress>www.gbean.org</siteAddress>
+    <siteDirectory>/home/projects/gbean/public_html/maven</siteDirectory>
+
+    <mailingLists>
+        <mailingList>
+            <name>gbean developers</name>
+            <subscribe>mailto:dev-subscribe@gbean.org</subscribe>
+            <unsubscribe>mailto:dev-unsubscribe@gbean.org</unsubscribe>
+        </mailingList>
+        <mailingList>
+            <name>gbean users</name>
+            <subscribe>mailto:user-subscribe@gbean.org</subscribe>
+            <unsubscribe>mailto:user-unsubscribe@gbean.org</unsubscribe>
+        </mailingList>
+        <mailingList>
+            <name>gbean source control messages</name>
+            <subscribe>mailto:scm-subscribe@gbean.org</subscribe>
+            <unsubscribe>mailto:scm-unsubscribe@gbean.org</unsubscribe>
+        </mailingList>
+    </mailingLists>
+
+    <dependencies>
+        <dependency>
+            <groupId>cglib</groupId>
+            <artifactId>cglib-nodep</artifactId>
+            <version>2.1</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-logging</groupId>
+            <artifactId>commons-logging</artifactId>
+            <version>1.0.4</version>
+        </dependency>
+        <dependency>
+            <groupId>log4j</groupId>
+            <artifactId>log4j</artifactId>
+            <version>1.2.8</version>
+        </dependency>
+        <dependency>
+            <groupId>geronimo</groupId>
+            <artifactId>geronimo-kernel</artifactId>
+            <version>1.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>mx4j</groupId>
+            <artifactId>mx4j</artifactId>
+            <version>3.0.1</version>
+        </dependency>
+        <dependency>
+            <groupId>springframework</groupId>
+            <artifactId>spring</artifactId>
+            <version>1.2</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <sourceDirectory>src/java</sourceDirectory>
+        <resources>
+            <resource>
+                <directory>${basedir}/src/resources</directory>
+                <includes>
+                    <include>**/*</include>
+                </includes>
+            </resource>
+        </resources>
+
+        <unitTestSourceDirectory>src/test</unitTestSourceDirectory>
+        <unitTest>
+            <includes>
+                <include>**/*Test.java</include>
+            </includes>
+            <resources>
+                <resource>
+                    <directory>${basedir}/src/test</directory>
+                    <includes>
+                        <include>**/*.properties</include>
+                    </includes>
+                </resource>
+            </resources>
+        </unitTest>
+    </build>
+
+    <reports>
+        <!-- <report>maven-license-plugin</report> -->
+        <!-- <report>maven-checkstyle-plugin</report>-->
+        <!-- <report>maven-pmd-plugin</report> -->
+        <!-- <report>maven-simian-plugin</report> -->
+        <!-- <report>maven-jdepend-plugin</report> -->
+        <!-- <report>maven-changelog-plugin</report> -->
+        <!-- <report>maven-statcvs-plugin</report> -->
+        <!-- <report>maven-file-activity-plugin</report> -->
+        <!-- <report>maven-developer-activity-plugin</report> -->
+        <report>maven-jxr-plugin</report>
+        <report>maven-javadoc-plugin</report>
+        <report>maven-junit-report-plugin</report>
+        <!-- <report>maven-faq-plugin</report> -->
+        <!-- <report>maven-clover-plugin</report>-->
+        <!-- <report>maven-changes-plugin</report>-->
+    </reports>
+
+</project>
+
diff --git a/kernel/src/java/org/gbean/beans/LiveCollection.java b/kernel/src/java/org/gbean/beans/LiveCollection.java
new file mode 100644
index 0000000..0b23578
--- /dev/null
+++ b/kernel/src/java/org/gbean/beans/LiveCollection.java
@@ -0,0 +1,32 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.gbean.beans;
+
+import java.util.Collection;
+
+/**
+ * An extension of collection that allows a client to register for notifications when
+ * members are added to and removed from the collection.
+ * 
+ * @version $Rev: 46019 $ $Date: 2004-09-14 02:56:06 -0700 (Tue, 14 Sep 2004) $
+ */
+public interface LiveCollection extends Collection {
+    void addReferenceCollectionListener(LiveCollectionListener listener);
+
+    void removeReferenceCollectionListener(LiveCollectionListener listener);
+}
diff --git a/kernel/src/java/org/gbean/beans/LiveCollectionEvent.java b/kernel/src/java/org/gbean/beans/LiveCollectionEvent.java
new file mode 100644
index 0000000..12229ba
--- /dev/null
+++ b/kernel/src/java/org/gbean/beans/LiveCollectionEvent.java
@@ -0,0 +1,39 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.gbean.beans;
+
+/**
+ * @version $Rev: 46019 $ $Date: 2004-09-14 02:56:06 -0700 (Tue, 14 Sep 2004) $
+ */
+public class LiveCollectionEvent {
+    private final String referenceName;
+    private final Object member;
+
+    public LiveCollectionEvent(String referenceName, Object member) {
+        this.referenceName = referenceName;
+        this.member = member;
+    }
+
+    public String getReferenceName() {
+        return referenceName;
+    }
+
+    public Object getMember() {
+        return member;
+    }
+}
diff --git a/kernel/src/java/org/gbean/beans/LiveCollectionListener.java b/kernel/src/java/org/gbean/beans/LiveCollectionListener.java
new file mode 100644
index 0000000..8cb3045
--- /dev/null
+++ b/kernel/src/java/org/gbean/beans/LiveCollectionListener.java
@@ -0,0 +1,29 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.gbean.beans;
+
+import java.util.EventListener;
+
+/**
+ * @version $Rev: 46019 $ $Date: 2004-09-14 02:56:06 -0700 (Tue, 14 Sep 2004) $
+ */
+public interface LiveCollectionListener extends EventListener {
+    void memberAdded(LiveCollectionEvent event);
+
+    void memberRemoved(LiveCollectionEvent event);
+}
diff --git a/kernel/src/java/org/gbean/beans/LiveHashSet.java b/kernel/src/java/org/gbean/beans/LiveHashSet.java
new file mode 100644
index 0000000..aa0eb71
--- /dev/null
+++ b/kernel/src/java/org/gbean/beans/LiveHashSet.java
@@ -0,0 +1,288 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.beans;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import javax.management.ObjectName;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.gbean.kernel.Kernel;
+import org.gbean.kernel.KernelUtil;
+import org.gbean.kernel.LifecycleAdapter;
+import org.gbean.kernel.LifecycleListener;
+import org.gbean.kernel.ServiceNotFoundException;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class LiveHashSet implements LiveCollection, Set {
+    private static final Log log = LogFactory.getLog(LiveHashSet.class);
+    private final Kernel kernel;
+    private final String name;
+    private final Set patterns;
+    private final Map members = new HashMap();
+    private final Set listeners = new HashSet();
+    private boolean stopped = true;
+    private LifecycleListener listener;
+
+    public LiveHashSet(Kernel kernel, String name, Set patterns) {
+        this.kernel = kernel;
+        this.name = name;
+        this.patterns = patterns;
+    }
+
+    public void start() {
+        synchronized (this) {
+            if (!stopped) {
+                throw new IllegalStateException("Already started");
+            }
+        }
+
+        Set targets = KernelUtil.getRunningServiceNames(kernel, patterns);
+        for (Iterator iterator = targets.iterator(); iterator.hasNext();) {
+            addTarget((ObjectName) iterator.next());
+        }
+
+        listener = new CollectionReferenceLifecycleListener();
+        kernel.addLifecycleListener(listener, patterns);
+        stopped = false;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public synchronized void destroy() {
+        stopped = true;
+        members.clear();
+        listeners.clear();
+
+        if (listener != null) {
+            kernel.removeLifecycleListener(listener);
+            listener = null;
+        }
+    }
+
+    private void addTarget(ObjectName target) {
+        Object service = null;
+        ArrayList listenerCopy;
+        synchronized (this) {
+            // if this is not a new target return
+            if (members.containsKey(target)) {
+                return;
+            }
+
+            service = getServiceReference(target);
+            if (service == null) {
+                return;
+            }
+            members.put(target, service);
+
+            // make a snapshot of the listeners
+            listenerCopy = new ArrayList(listeners);
+        }
+
+        // fire the member added event
+        for (Iterator iterator = listenerCopy.iterator(); iterator.hasNext();) {
+            LiveCollectionListener listener = (LiveCollectionListener) iterator.next();
+            try {
+                listener.memberAdded(new LiveCollectionEvent(name, service));
+            } catch (Throwable t) {
+                log.error("Listener threw exception", t);
+            }
+        }
+    }
+
+    protected Object getServiceReference(ObjectName target) {
+        try {
+            return kernel.getService(target);
+        } catch (ServiceNotFoundException e) {
+            // service was removed before we could add it
+            return null;
+        }
+    }
+
+    private void removeTarget(ObjectName target) {
+        Object service;
+        ArrayList listenerCopy;
+        synchronized (this) {
+            // remove the proxy
+            service = members.remove(target);
+
+            // if this was not a target return
+            if (service == null) {
+                return;
+            }
+
+            // make a snapshot of the listeners
+            listenerCopy = new ArrayList(listeners);
+        }
+
+        // fire the member removed event
+        for (Iterator iterator = listenerCopy.iterator(); iterator.hasNext();) {
+            LiveCollectionListener listener = (LiveCollectionListener) iterator.next();
+            try {
+                listener.memberRemoved(new LiveCollectionEvent(name, service));
+            } catch (Throwable t) {
+                log.error("Listener threw exception", t);
+            }
+        }
+    }
+
+    public synchronized boolean isStopped() {
+        return stopped;
+    }
+
+    public synchronized void addReferenceCollectionListener(LiveCollectionListener listener) {
+        listeners.add(listener);
+    }
+
+    public synchronized void removeReferenceCollectionListener(LiveCollectionListener listener) {
+        listeners.remove(listener);
+    }
+
+    public synchronized int size() {
+        if (stopped) {
+            return 0;
+        }
+        return members.size();
+    }
+
+    public synchronized boolean isEmpty() {
+        if (stopped) {
+            return true;
+        }
+        return members.isEmpty();
+    }
+
+    public synchronized boolean contains(Object o) {
+        if (stopped) {
+            return false;
+        }
+        return members.containsValue(o);
+    }
+
+    public synchronized Iterator iterator() {
+        if (stopped) {
+            return new Iterator() {
+                public boolean hasNext() {
+                    return false;
+                }
+
+                public Object next() {
+                    throw new NoSuchElementException();
+                }
+
+                public void remove() {
+                    throw new UnsupportedOperationException();
+                }
+            };
+        }
+
+        return new Iterator() {
+            // copy the proxies, so the client can iterate without concurrent modification
+            // this is necssary since the client has nothing to synchronize on
+            private final Iterator iterator = new ArrayList(members.values()).iterator();
+
+            public boolean hasNext() {
+                return iterator.hasNext();
+            }
+
+            public Object next() {
+                return iterator.next();
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    public synchronized Object[] toArray() {
+        if (stopped) {
+            return new Object[0];
+        }
+        return members.values().toArray();
+    }
+
+    public synchronized Object[] toArray(Object a[]) {
+        if (stopped) {
+            if (a.length > 0) {
+                a[0] = null;
+            }
+            return a;
+        }
+        return members.values().toArray(a);
+    }
+
+    public synchronized boolean containsAll(Collection c) {
+        if (stopped) {
+            return c.isEmpty();
+        }
+        return members.values().containsAll(c);
+    }
+
+    public boolean add(Object o) {
+        throw new UnsupportedOperationException();
+    }
+
+    public boolean remove(Object o) {
+        throw new UnsupportedOperationException();
+    }
+
+    public boolean addAll(Collection c) {
+        throw new UnsupportedOperationException();
+    }
+
+    public boolean removeAll(Collection c) {
+        throw new UnsupportedOperationException();
+    }
+
+    public boolean retainAll(Collection c) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void clear() {
+        throw new UnsupportedOperationException();
+    }
+
+    private class CollectionReferenceLifecycleListener extends LifecycleAdapter {
+        public void running(ObjectName objectName) {
+            addTarget(objectName);
+        }
+
+        public void stopping(ObjectName objectName) {
+            removeTarget(objectName);
+        }
+
+        public void stopped(ObjectName objectName) {
+            removeTarget(objectName);
+        }
+
+        public void unloaded(ObjectName objectName) {
+            removeTarget(objectName);
+        }
+    }
+}
diff --git a/kernel/src/java/org/gbean/beans/LiveProxyHashSet.java b/kernel/src/java/org/gbean/beans/LiveProxyHashSet.java
new file mode 100644
index 0000000..5e1d7ec
--- /dev/null
+++ b/kernel/src/java/org/gbean/beans/LiveProxyHashSet.java
@@ -0,0 +1,51 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.beans;
+
+import java.util.Set;
+import javax.management.ObjectName;
+
+import org.gbean.kernel.Kernel;
+import org.gbean.kernel.ServiceNotFoundException;
+import org.gbean.proxy.ProxyFactory;
+import org.gbean.proxy.ProxyManager;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class LiveProxyHashSet extends LiveHashSet {
+    private final ProxyFactory factory;
+
+    public LiveProxyHashSet(Kernel kernel, String name, Set patterns, Class type) {
+        super (kernel, name, patterns);
+
+        try {
+            factory = ProxyManager.findProxyManager(kernel).createProxyFactory(type);
+        } catch (ServiceNotFoundException e) {
+            throw new IllegalStateException("No ProxyManager available in kernel");
+        }
+    }
+
+    protected Object getServiceReference(ObjectName target) {
+        try {
+            return factory.createProxy(target);
+        } catch (ServiceNotFoundException e) {
+            // service was removed before we could add it
+            return null;
+        }
+    }
+}
diff --git a/kernel/src/java/org/gbean/classloader/DestroyableClassLoader.java b/kernel/src/java/org/gbean/classloader/DestroyableClassLoader.java
new file mode 100644
index 0000000..50a5f55
--- /dev/null
+++ b/kernel/src/java/org/gbean/classloader/DestroyableClassLoader.java
@@ -0,0 +1,24 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.classloader;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public interface DestroyableClassLoader {
+    void destroy();
+}
diff --git a/kernel/src/java/org/gbean/classloader/JarFileClassLoader.java b/kernel/src/java/org/gbean/classloader/JarFileClassLoader.java
new file mode 100644
index 0000000..36830d1
--- /dev/null
+++ b/kernel/src/java/org/gbean/classloader/JarFileClassLoader.java
@@ -0,0 +1,324 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.classloader;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.CodeSource;
+import java.security.cert.Certificate;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class JarFileClassLoader extends NamedClassLoader {
+    private final Object lock = new Object();
+    private final LinkedHashMap classPath;
+    private boolean destroyed = false;
+
+    public JarFileClassLoader(String name, List urls) {
+        this(name, urls, Thread.currentThread().getContextClassLoader());
+    }
+
+    public JarFileClassLoader(String name, List urls, ClassLoader parent) {
+        super(name, new URL[0], parent);
+
+        classPath = new LinkedHashMap();
+        addURLs(urls);
+    }
+
+    public URL[] getURLs() {
+        return (URL[]) classPath.keySet().toArray(new URL[classPath.keySet().size()]);
+    }
+
+    protected void addURL(URL url) {
+        addURLs(Collections.singletonList(url));
+    }
+
+    protected void addURLs(List urls) {
+        LinkedList locationStack = new LinkedList(urls);
+        try {
+            while (!locationStack.isEmpty()) {
+                URL url = (URL) locationStack.removeFirst();
+
+                if (!"file".equals(url.getProtocol())) {
+                    // download the jar
+                    throw new Error("Only local file jars are supported " + url);
+                }
+
+                String path = url.getPath();
+                if (classPath.containsKey(path)) {
+                    continue;
+                }
+
+                File file = new File(path);
+                if (!file.canRead()) {
+                    // can't read file...
+                    continue;
+                }
+
+                // open the jar file
+                JarFile jarFile = null;
+                try {
+                    jarFile = new JarFile(file);
+                } catch (IOException e) {
+                    // can't seem to open the file
+                    continue;
+                }
+                classPath.put(url, jarFile);
+
+                // push the manifest classpath on the stack (make sure to maintain the order)
+                Manifest manifest = null;
+                try {
+                    manifest = jarFile.getManifest();
+                } catch (IOException ignored) {
+                }
+
+                if (manifest != null) {
+                    Attributes mainAttributes = manifest.getMainAttributes();
+                    String manifestClassPath = mainAttributes.getValue(Attributes.Name.CLASS_PATH);
+                    if (manifestClassPath != null) {
+                        LinkedList classPathUrls = new LinkedList();
+                        for (StringTokenizer tokenizer = new StringTokenizer(manifestClassPath, " "); tokenizer.hasMoreTokens();) {
+                            String entry = tokenizer.nextToken();
+                            File parentDir = file.getParentFile();
+                            File entryFile = new File(parentDir, entry);
+                            // manifest entries are optional... if they aren't there it is ok
+                            if (entryFile.canRead()) {
+                                classPathUrls.addLast(entryFile.getAbsolutePath());
+                            }
+                        }
+                        locationStack.addAll(0, classPathUrls);
+                    }
+                }
+            }
+        } catch (Error e) {
+            destroy();
+            throw e;
+        }
+    }
+
+    public void destroy() {
+        synchronized (lock) {
+            if (destroyed) {
+                return;
+            }
+            destroyed = true;
+            for (Iterator iterator = classPath.values().iterator(); iterator.hasNext();) {
+                JarFile jarFile = (JarFile) iterator.next();
+                try {
+                    jarFile.close();
+                } catch (IOException ignored) {
+                }
+            }
+            classPath.clear();
+        }
+        super.destroy();
+    }
+
+    public URL findResource(String resourceName) {
+        URL jarUrl = null;
+        synchronized (lock) {
+            if (destroyed) {
+                return null;
+            }
+            for (Iterator iterator = classPath.entrySet().iterator(); iterator.hasNext() && jarUrl == null;) {
+                Map.Entry entry = (Map.Entry) iterator.next();
+                JarFile jarFile = (JarFile) entry.getValue();
+                JarEntry jarEntry = jarFile.getJarEntry(resourceName);
+                if (jarEntry != null && !jarEntry.isDirectory()) {
+                    jarUrl = (URL) entry.getKey();
+                }
+            }
+        }
+
+
+        try {
+            String urlString = "jar:" + jarUrl + "!/" + resourceName;
+            return new URL(jarUrl, urlString);
+        } catch (MalformedURLException e) {
+            return null;
+        }
+    }
+
+    public Enumeration findResources(String resourceName) throws IOException {
+        LinkedList resources = new LinkedList();
+        synchronized (lock) {
+            if (destroyed) {
+                return null;
+            }
+            for (Iterator iterator = classPath.entrySet().iterator(); iterator.hasNext();) {
+                Map.Entry entry = (Map.Entry) iterator.next();
+                JarFile jarFile = (JarFile) entry.getValue();
+                JarEntry jarEntry = jarFile.getJarEntry(resourceName);
+                if (jarEntry != null && !jarEntry.isDirectory()) {
+                    try {
+                        URL url = (URL) entry.getKey();
+                        String urlString = "jar:" + url + "!/" + resourceName;
+                        resources.add(new URL(url, urlString));
+                    } catch (MalformedURLException e) {
+                    }
+                }
+            }
+        }
+
+        return Collections.enumeration(resources);
+    }
+
+    protected Class findClass(String className) throws ClassNotFoundException {
+        SecurityManager securityManager = System.getSecurityManager();
+        if (securityManager != null) {
+            String packageName = null;
+            int packageEnd = className.lastIndexOf('.');
+            if (packageEnd >= 0) {
+                packageName = className.substring(0, packageEnd);
+                securityManager.checkPackageDefinition(packageName);
+            }
+        }
+
+        Certificate[] certificates = null;
+        URL jarUrl = null;
+        Manifest manifest = null;
+        byte[] bytes;
+        synchronized (lock) {
+            if (destroyed) {
+                throw new ClassNotFoundException("Class loader has been destroyed: " + className);
+            }
+
+            try {
+                String entryName = className.replace('.', '/') + ".class";
+                InputStream inputStream = null;
+                for (Iterator iterator = classPath.entrySet().iterator(); iterator.hasNext() && inputStream == null;) {
+                    Map.Entry entry = (Map.Entry) iterator.next();
+                    jarUrl = (URL) entry.getKey();
+                    JarFile jarFile = (JarFile) entry.getValue();
+                    JarEntry jarEntry = jarFile.getJarEntry(entryName);
+                    if (jarEntry != null && !jarEntry.isDirectory()) {
+                        inputStream = jarFile.getInputStream(jarEntry);
+                        certificates = jarEntry.getCertificates();
+                        manifest = jarFile.getManifest();
+                    }
+                }
+                if (inputStream == null) {
+                    throw new ClassNotFoundException(className);
+                }
+
+                try {
+                    byte[] buffer = new byte[4096];
+                    ByteArrayOutputStream out = new ByteArrayOutputStream();
+                    for (int count = inputStream.read(buffer); count >= 0; count = inputStream.read(buffer)) {
+                        out.write(buffer, 0, count);
+                    }
+                    bytes = out.toByteArray();
+                } finally {
+                    if (inputStream != null) {
+                        inputStream.close();
+                    }
+                }
+            } catch (IOException e) {
+                throw new ClassNotFoundException(className, e);
+            }
+        }
+
+        definePackage(className, jarUrl, manifest);
+        CodeSource codeSource = new CodeSource(jarUrl, certificates);
+        Class clazz = defineClass(className, bytes, 0, bytes.length, codeSource);
+        return clazz;
+    }
+
+    private void definePackage(String className, URL jarUrl, Manifest manifest) {
+        int packageEnd = className.lastIndexOf('.');
+        if (packageEnd < 0) {
+            return;
+        }
+
+        String packageName = className.substring(0, packageEnd);
+        String packagePath = packageName.replace('.', '/') + "/";
+
+        Attributes packageAttributes = null;
+        Attributes mainAttributes = null;
+        if (manifest != null) {
+            packageAttributes = manifest.getAttributes(packagePath);
+            mainAttributes = manifest.getMainAttributes();
+        }
+        Package pkg = getPackage(packageName);
+        if (pkg != null) {
+            if (pkg.isSealed()) {
+                if (!pkg.isSealed(jarUrl)) {
+                    throw new SecurityException("Package was already sealed with another URL: package=" + packageName + ", url=" + jarUrl);
+                }
+            } else {
+                if (isSealed(packageAttributes, mainAttributes)) {
+                    throw new SecurityException("Package was already been loaded and not sealed: package=" + packageName + ", url=" + jarUrl);
+                }
+            }
+        } else {
+            String specTitle = getAttribute(Attributes.Name.SPECIFICATION_TITLE, packageAttributes, mainAttributes);
+            String specVendor = getAttribute(Attributes.Name.SPECIFICATION_VENDOR, packageAttributes, mainAttributes);
+            String specVersion = getAttribute(Attributes.Name.SPECIFICATION_VERSION, packageAttributes, mainAttributes);
+            String implTitle = getAttribute(Attributes.Name.IMPLEMENTATION_TITLE, packageAttributes, mainAttributes);
+            String implVendor = getAttribute(Attributes.Name.IMPLEMENTATION_VENDOR, packageAttributes, mainAttributes);
+            String implVersion = getAttribute(Attributes.Name.IMPLEMENTATION_VERSION, packageAttributes, mainAttributes);
+
+            URL sealBase = null;
+            if (isSealed(packageAttributes, mainAttributes)) {
+                sealBase = jarUrl;
+            }
+
+            definePackage(packageName, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor, sealBase);
+        }
+    }
+
+    private String getAttribute(Attributes.Name name, Attributes packageAttributes, Attributes mainAttributes) {
+        if (packageAttributes != null) {
+            String value = packageAttributes.getValue(name);
+            if (value != null) {
+                return value;
+            }
+        }
+        if (mainAttributes != null) {
+            return mainAttributes.getValue(name);
+        }
+        return null;
+    }
+
+    private boolean isSealed(Attributes packageAttributes, Attributes mainAttributes) {
+        String sealed = getAttribute(Attributes.Name.SEALED, packageAttributes, mainAttributes);
+        if (sealed == null) {
+            return false;
+        }
+        if (sealed == null) {
+            return false;
+        }
+        return "true".equalsIgnoreCase(sealed);
+    }
+}
\ No newline at end of file
diff --git a/kernel/src/java/org/gbean/classloader/NamedClassLoader.java b/kernel/src/java/org/gbean/classloader/NamedClassLoader.java
new file mode 100644
index 0000000..78b8094
--- /dev/null
+++ b/kernel/src/java/org/gbean/classloader/NamedClassLoader.java
@@ -0,0 +1,87 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.classloader;
+
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.lang.reflect.Field;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.net.URLStreamHandlerFactory;
+import java.util.Map;
+
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class NamedClassLoader extends URLClassLoader implements DestroyableClassLoader {
+    private final String name;
+
+    public NamedClassLoader(String name, URL[] urls) {
+        super(urls);
+        this.name = name;
+    }
+
+    public NamedClassLoader(String name, URL[] urls, ClassLoader parent) {
+        super(urls, parent);
+        this.name = name;
+    }
+
+    public NamedClassLoader(String name, URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) {
+        super(urls, parent, factory);
+        this.name = name;
+    }
+
+    public void destroy() {
+        LogFactory.release(this);
+        clearSoftCache(ObjectInputStream.class, "subclassAudits");
+        clearSoftCache(ObjectOutputStream.class, "subclassAudits");
+        clearSoftCache(ObjectStreamClass.class, "localDescs");
+        clearSoftCache(ObjectStreamClass.class, "reflectors");
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    private static Object clearSoftCacheLock = new Object();
+    private static boolean clearSoftCacheFailed = false;
+    private void clearSoftCache(Class clazz, String fieldName) {
+        Map cache = null;
+        try {
+            Field f = clazz.getDeclaredField(fieldName);
+            f.setAccessible(true);
+            cache = (Map) f.get(null);
+        } catch (Throwable e) {
+            synchronized (clearSoftCacheLock) {
+                // only print the failed message once per vm
+                if (!clearSoftCacheFailed) {
+                    clearSoftCacheFailed = true;
+                    LogFactory.getLog(JarFileClassLoader.class).error("Unable to clear SoftCache field " + fieldName + " in class " + clazz);
+                }
+            }
+        }
+
+        if (cache != null) {
+            synchronized (cache) {
+                cache.clear();
+            }
+        }
+    }
+}
diff --git a/kernel/src/java/org/gbean/configuration/Configuration.java b/kernel/src/java/org/gbean/configuration/Configuration.java
new file mode 100644
index 0000000..200f753
--- /dev/null
+++ b/kernel/src/java/org/gbean/configuration/Configuration.java
@@ -0,0 +1,33 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.configuration;
+
+import java.net.URI;
+import java.util.Set;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public interface Configuration {
+    URI getConfigurationId();
+
+    URI getParentId();
+
+    ClassLoader getClassLoader();
+
+    Set getServiceNames();
+}
diff --git a/kernel/src/java/org/gbean/configuration/ConfigurationInfo.java b/kernel/src/java/org/gbean/configuration/ConfigurationInfo.java
new file mode 100644
index 0000000..b6b685e
--- /dev/null
+++ b/kernel/src/java/org/gbean/configuration/ConfigurationInfo.java
@@ -0,0 +1,114 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.configuration;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class ConfigurationInfo {
+    /**
+     * URI used to referr to this configuration in the configuration manager
+     */
+    private URI configurationId;
+
+    /**
+     * The uri of the parent of this configuration.  May be null.
+     */
+    private URI parentId;
+
+    /**
+     * The domain name of the configurations.  This is used to autogenerate names for sub components.
+     */
+    private String domain;
+
+    /**
+     * The server name of the configurations.  This is used to autogenerate names for sub components.
+     */
+    private String server;
+
+    /**
+     * List of URIs of jar files on which this configuration is dependent on.
+     */
+    private final LinkedHashSet dependencies = new LinkedHashSet();
+
+    public ConfigurationInfo() {
+    }
+
+    public ConfigurationInfo(ConfigurationInfo configurationInfo) {
+        configurationId = configurationInfo.configurationId;
+        parentId = configurationInfo.getParentId();
+        domain = configurationInfo.domain;
+        server = configurationInfo.server;
+        setDependencies(new ArrayList(configurationInfo.dependencies));
+    }
+
+    public URI getConfigurationId() {
+        return configurationId;
+    }
+
+    public void setConfigurationId(URI configurationId) {
+        this.configurationId = configurationId;
+    }
+
+    public URI getParentId() {
+        return parentId;
+    }
+
+    public void setParentId(URI parentId) {
+        this.parentId = parentId;
+    }
+
+    public String getDomain() {
+        return domain;
+    }
+
+    public void setDomain(String domain) {
+        this.domain = domain;
+    }
+
+    public String getServer() {
+        return server;
+    }
+
+    public void setServer(String server) {
+        this.server = server;
+    }
+
+    public List getDependencies() {
+        return Collections.unmodifiableList(new ArrayList(dependencies));
+    }
+
+    public void setDependencies(List dependencies) {
+        this.dependencies.clear();
+        for (Iterator iterator = dependencies.iterator(); iterator.hasNext();) {
+            URI dependency = (URI) iterator.next();
+            addDependency(dependency);
+        }
+    }
+
+    public void addDependency(URI dependency) {
+        assert dependency != null;
+        this.dependencies.add(dependency);
+    }
+}
diff --git a/kernel/src/java/org/gbean/configuration/ConfigurationUtil.java b/kernel/src/java/org/gbean/configuration/ConfigurationUtil.java
new file mode 100644
index 0000000..59b279d
--- /dev/null
+++ b/kernel/src/java/org/gbean/configuration/ConfigurationUtil.java
@@ -0,0 +1,58 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.configuration;
+
+import java.net.URI;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.gbean.classloader.NamedClassLoader;
+import org.gbean.repository.Repository;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public final class ConfigurationUtil {
+    private ConfigurationUtil() {
+    }
+
+    public static ClassLoader createClassLoader(String name, List dependencies, ClassLoader parentClassLoader, Collection repositories) {
+        List urls = new LinkedList();
+        for (Iterator i = dependencies.iterator(); i.hasNext();) {
+            URI uri = (URI) i.next();
+            URL url = null;
+            for (Iterator j = repositories.iterator(); j.hasNext();) {
+                Repository repository = (Repository) j.next();
+                if (repository.containsResource(uri)) {
+                    url = repository.getResource(uri);
+                    break;
+                }
+            }
+            if (url == null) {
+                throw new MissingDependencyException("Unable to resolve dependency " + uri);
+            }
+            if (!"file".equals(url.getProtocol())) {
+                throw new MissingDependencyException("Only local file jars are supported " + url);
+            }
+            urls.add(url);
+        }
+        return new NamedClassLoader(name, (URL[]) urls.toArray(new URL[urls.size()]), parentClassLoader);
+    }
+}
diff --git a/kernel/src/java/org/gbean/configuration/InvalidConfigurationException.java b/kernel/src/java/org/gbean/configuration/InvalidConfigurationException.java
new file mode 100644
index 0000000..0387114
--- /dev/null
+++ b/kernel/src/java/org/gbean/configuration/InvalidConfigurationException.java
@@ -0,0 +1,37 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.configuration;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class InvalidConfigurationException extends RuntimeException {
+    public InvalidConfigurationException() {
+    }
+
+    public InvalidConfigurationException(String s) {
+        super(s);
+    }
+
+    public InvalidConfigurationException(String s, Throwable throwable) {
+        super(s, throwable);
+    }
+
+    public InvalidConfigurationException(Throwable throwable) {
+        super(throwable);
+    }
+}
diff --git a/kernel/src/java/org/gbean/configuration/MissingDependencyException.java b/kernel/src/java/org/gbean/configuration/MissingDependencyException.java
new file mode 100644
index 0000000..25c6667
--- /dev/null
+++ b/kernel/src/java/org/gbean/configuration/MissingDependencyException.java
@@ -0,0 +1,37 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.configuration;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class MissingDependencyException extends RuntimeException {
+    public MissingDependencyException() {
+    }
+
+    public MissingDependencyException(String s) {
+        super(s);
+    }
+
+    public MissingDependencyException(String s, Throwable throwable) {
+        super(s, throwable);
+    }
+
+    public MissingDependencyException(Throwable throwable) {
+        super(throwable);
+    }
+}
diff --git a/kernel/src/java/org/gbean/geronimo/CollectionReference.java b/kernel/src/java/org/gbean/geronimo/CollectionReference.java
new file mode 100644
index 0000000..9d4c107
--- /dev/null
+++ b/kernel/src/java/org/gbean/geronimo/CollectionReference.java
@@ -0,0 +1,115 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.gbean.geronimo;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+import javax.management.ObjectName;
+
+import org.gbean.kernel.ClassLoading;
+import org.gbean.service.ServiceContext;
+import org.gbean.spring.ServiceContextThreadLocal;
+import org.springframework.beans.MutablePropertyValues;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+
+/**
+ * @version $Rev: 71492 $ $Date: 2004-11-14 21:31:50 -0800 (Sun, 14 Nov 2004) $
+ */
+public class CollectionReference implements FactoryBean, Serializable {
+    public static BeanDefinitionHolder createBeanDefinition(String name, Set patterns, String type) {
+        RootBeanDefinition beanDefinition = new RootBeanDefinition(CollectionReference.class, 0);
+        MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
+        propertyValues.addPropertyValue("name", name);
+        propertyValues.addPropertyValue("patterns", patterns);
+        propertyValues.addPropertyValue("type", type);
+        return new BeanDefinitionHolder(beanDefinition, CollectionReference.class.getName());
+    }
+
+    /**
+     * Name of this reference.
+     */
+    private String name;
+
+    /**
+     * Proxy type which is injected into the service.
+     */
+    private String type;
+
+    /**
+     * The target objectName patterns to watch for a connection.
+     */
+    private Set patterns;
+
+    public CollectionReference() {
+    }
+
+    public CollectionReference(String name, Set patterns, String type) {
+        this.name = name;
+        this.patterns = patterns;
+        this.type = type;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public Set getPatterns() {
+        return patterns;
+    }
+
+    public void setPatterns(Set patterns) {
+        this.patterns = patterns;
+    }
+
+    public void setPattern(ObjectName pattern) {
+        this.patterns = Collections.singleton(pattern);
+    }
+
+    public final Class getObjectType() {
+        return Collection.class;
+    }
+
+    public synchronized final Object getObject() throws ClassNotFoundException {
+        ServiceContext serviceContext = ServiceContextThreadLocal.get();
+        if (serviceContext == null) {
+            throw new IllegalStateException("Service context has not been set");
+        }
+        Class proxyType = ClassLoading.loadClass(type, serviceContext.getClassLoader());
+        return new ProxyReferenceCollection(this, name, proxyType, serviceContext.getKernel(), patterns);
+    }
+
+    public boolean isSingleton() {
+        return true;
+    }
+}
diff --git a/kernel/src/java/org/gbean/geronimo/DependencyManagerBridge.java b/kernel/src/java/org/gbean/geronimo/DependencyManagerBridge.java
new file mode 100644
index 0000000..b37e98a
--- /dev/null
+++ b/kernel/src/java/org/gbean/geronimo/DependencyManagerBridge.java
@@ -0,0 +1,77 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.geronimo;
+
+import java.util.Set;
+import java.util.Collection;
+import javax.management.ObjectName;
+
+import org.gbean.kernel.DependencyManager;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class DependencyManagerBridge implements org.apache.geronimo.kernel.DependencyManager {
+    private final DependencyManager dependencyManager;
+
+    public DependencyManagerBridge(DependencyManager dependencyManager) {
+        this.dependencyManager = dependencyManager;
+    }
+
+    public void close() {
+    }
+
+    public void addDependency(ObjectName child, ObjectName parent) {
+        dependencyManager.addDependency(child, parent);
+    }
+
+    public void removeDependency(ObjectName child, ObjectName parent) {
+        dependencyManager.removeDependency(child, parent);
+    }
+
+    public void removeAllDependencies(ObjectName child) {
+        dependencyManager.removeAllDependencies(child);
+    }
+
+    public void addDependencies(ObjectName child, Set parents) {
+        dependencyManager.addDependencies(child, parents);
+    }
+
+    public Set getParents(ObjectName child) {
+        return dependencyManager.getParents(child);
+    }
+
+    public Set getChildren(ObjectName parent) {
+        return dependencyManager.getChildren(parent);
+    }
+
+    public void addStartHolds(ObjectName objectName, Collection holds) {
+        dependencyManager.addStartHolds(objectName, holds);
+    }
+
+    public void removeStartHolds(ObjectName objectName, Collection holds) {
+        dependencyManager.removeStartHolds(objectName, holds);
+    }
+
+    public void removeAllStartHolds(ObjectName objectName) {
+        dependencyManager.removeAllStartHolds(objectName);
+    }
+
+    public ObjectName checkBlocker(ObjectName objectName) {
+        return dependencyManager.checkBlocker(objectName);
+    }
+}
diff --git a/kernel/src/java/org/gbean/geronimo/DynamicGBeanProcessor.java b/kernel/src/java/org/gbean/geronimo/DynamicGBeanProcessor.java
new file mode 100644
index 0000000..a554198
--- /dev/null
+++ b/kernel/src/java/org/gbean/geronimo/DynamicGBeanProcessor.java
@@ -0,0 +1,62 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.geronimo;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.geronimo.gbean.DynamicGBean;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanInitializationException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class DynamicGBeanProcessor implements BeanPostProcessor {
+    private final String beanName;
+    private final Map dynamicProperties;
+
+    public DynamicGBeanProcessor(String beanName, Map dynamicProperties) {
+        this.beanName = beanName;
+        this.dynamicProperties = dynamicProperties;
+    }
+
+    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+        if (this.beanName.equals(beanName)) {
+            for (Iterator iterator = dynamicProperties.entrySet().iterator(); iterator.hasNext();) {
+                Map.Entry entry = (Map.Entry) iterator.next();
+                String propertyName = (String) entry.getKey();
+                Object propertyValue = entry.getValue();
+                try {
+                    ((DynamicGBean) bean).setAttribute(propertyName, propertyValue);
+                } catch (Exception e) {
+                    throw new BeanInitializationException("Error invoking dynamic property setter method:" +
+                            " beanName " + beanName +
+                            " beanClass " + bean.getClass() +
+                            " propertyName " + propertyName +
+                            " propertyValue " + propertyValue, e);
+                }
+            }
+        }
+        return bean;
+    }
+
+    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+        return bean;
+    }
+}
diff --git a/kernel/src/java/org/gbean/geronimo/FactoryBeanProvider.java b/kernel/src/java/org/gbean/geronimo/FactoryBeanProvider.java
new file mode 100644
index 0000000..01c673c
--- /dev/null
+++ b/kernel/src/java/org/gbean/geronimo/FactoryBeanProvider.java
@@ -0,0 +1,26 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.geronimo;
+
+import org.springframework.beans.factory.FactoryBean;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public interface FactoryBeanProvider {
+    FactoryBean getFactoryBean();
+}
diff --git a/kernel/src/java/org/gbean/geronimo/GeronimoKernelReference.java b/kernel/src/java/org/gbean/geronimo/GeronimoKernelReference.java
new file mode 100644
index 0000000..98f8fb8
--- /dev/null
+++ b/kernel/src/java/org/gbean/geronimo/GeronimoKernelReference.java
@@ -0,0 +1,58 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.gbean.geronimo;
+
+import java.io.Serializable;
+
+import org.apache.geronimo.kernel.Kernel;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.gbean.service.ServiceContext;
+import org.gbean.spring.ServiceContextThreadLocal;
+import org.gbean.kernel.ServiceNotFoundException;
+
+/**
+ * @version $Rev: 71492 $ $Date: 2004-11-14 21:31:50 -0800 (Sun, 14 Nov 2004) $
+ */
+public class GeronimoKernelReference implements FactoryBean, Serializable {
+    public static BeanDefinitionHolder createBeanDefinition() {
+        RootBeanDefinition beanDefinition = new RootBeanDefinition(GeronimoKernelReference.class, 0);
+        return new BeanDefinitionHolder(beanDefinition, GeronimoKernelReference.class.getName());
+    }
+
+    public final Class getObjectType() {
+        return Kernel.class;
+    }
+
+    public synchronized final Object getObject() {
+        ServiceContext serviceContext = ServiceContextThreadLocal.get();
+        if (serviceContext == null) {
+            throw new IllegalStateException("Service context has not been set");
+        }
+        try {
+            return (Kernel) serviceContext.getKernel().getService(Kernel.KERNEL);
+        } catch (ServiceNotFoundException e) {
+            throw new IllegalStateException("A Geronimo kernel has not been loaded");
+        }
+    }
+
+    public boolean isSingleton() {
+        return true;
+    }
+}
diff --git a/kernel/src/java/org/gbean/geronimo/GeronimoLifecycleControllerReference.java b/kernel/src/java/org/gbean/geronimo/GeronimoLifecycleControllerReference.java
new file mode 100644
index 0000000..66b9fc7
--- /dev/null
+++ b/kernel/src/java/org/gbean/geronimo/GeronimoLifecycleControllerReference.java
@@ -0,0 +1,69 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.gbean.geronimo;
+
+import java.io.Serializable;
+
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.apache.geronimo.gbean.GBeanLifecycleController;
+import org.gbean.service.ServiceContext;
+import org.gbean.spring.ServiceContextThreadLocal;
+
+/**
+ * @version $Rev: 71492 $ $Date: 2004-11-14 21:31:50 -0800 (Sun, 14 Nov 2004) $
+ */
+public class GeronimoLifecycleControllerReference implements FactoryBean, Serializable {
+    public static BeanDefinitionHolder createBeanDefinition() {
+        RootBeanDefinition beanDefinition = new RootBeanDefinition(GeronimoLifecycleControllerReference.class, 0);
+        return new BeanDefinitionHolder(beanDefinition, GeronimoLifecycleControllerReference.class.getName());
+    }
+
+    public final Class getObjectType() {
+        return GBeanLifecycleController.class;
+    }
+
+    public synchronized final Object getObject() {
+        ServiceContext serviceContext = ServiceContextThreadLocal.get();
+        if (serviceContext == null) {
+            throw new IllegalStateException("Service context has not been set");
+        }
+        return new GBeanLifecycleControllerImpl(serviceContext);
+    }
+
+    public boolean isSingleton() {
+        return true;
+    }
+
+    private static class GBeanLifecycleControllerImpl implements GBeanLifecycleController {
+        private final ServiceContext serviceContext;
+
+        public GBeanLifecycleControllerImpl(ServiceContext serviceContext) {
+            this.serviceContext = serviceContext;
+        }
+
+        public int getState() {
+            return serviceContext.getState();
+        }
+
+        public void stop() throws Exception {
+            serviceContext.stop();
+        }
+    }
+}
diff --git a/kernel/src/java/org/gbean/geronimo/GeronimoMetadataProvider.java b/kernel/src/java/org/gbean/geronimo/GeronimoMetadataProvider.java
new file mode 100644
index 0000000..a94e29e
--- /dev/null
+++ b/kernel/src/java/org/gbean/geronimo/GeronimoMetadataProvider.java
@@ -0,0 +1,131 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.geronimo;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.geronimo.gbean.GAttributeInfo;
+import org.apache.geronimo.gbean.GBeanInfo;
+import org.apache.geronimo.gbean.GReferenceInfo;
+import org.gbean.kernel.ClassLoading;
+import org.gbean.kernel.ConstructorSignature;
+import org.gbean.metadata.ClassMetadata;
+import org.gbean.metadata.ConstructorMetadata;
+import org.gbean.metadata.MetadataProvider;
+import org.gbean.metadata.ParameterMetadata;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class GeronimoMetadataProvider implements MetadataProvider {
+    public void addClassMetadata(ClassMetadata classMetadata) {
+        Class type = classMetadata.getType();
+        GBeanInfo gbeanInfo = getGBeanInfo(type);
+        if (gbeanInfo == null) {
+            return;
+        }
+
+        classMetadata.put("j2eeType", gbeanInfo.getJ2eeType());
+
+        Set attributes = gbeanInfo.getAttributes();
+        Set references = gbeanInfo.getReferences();
+        Map attributeTypes = new HashMap(attributes.size() + references.size());
+        Set persistentProperties = new HashSet();
+        for (Iterator iterator = attributes.iterator(); iterator.hasNext();) {
+            GAttributeInfo attributeInfo = (GAttributeInfo) iterator.next();
+            Class attributeType = null;
+            try {
+                attributeType = ClassLoading.loadClass(attributeInfo.getType(), type.getClassLoader());
+            } catch (ClassNotFoundException ignored) {
+                // couldn't load the type.. just proceed and we'll skip the constructor declaration
+            }
+            attributeTypes.put(attributeInfo.getName(), attributeType);
+
+            if (attributeInfo.isPersistent()) {
+                persistentProperties.add(fixPropertyName(attributeInfo.getName()));
+            }
+        }
+
+        for (Iterator iterator = references.iterator(); iterator.hasNext();) {
+            GReferenceInfo referenceInfo = (GReferenceInfo) iterator.next();
+            Class attributeType = null;
+            try {
+                attributeType = ClassLoading.loadClass(referenceInfo.getProxyType(), type.getClassLoader());
+            } catch (ClassNotFoundException ignored) {
+                // couldn't load the type.. just proceed and we'll skip the constructor declaration
+            }
+            attributeTypes.put(referenceInfo.getName(), attributeType);
+            persistentProperties.add(fixPropertyName(referenceInfo.getName()));
+        }
+
+
+        List constructorArgNames = gbeanInfo.getConstructor().getAttributeNames();
+        List constructorArgTypes = new ArrayList(constructorArgNames.size());
+        for (Iterator iterator = constructorArgNames.iterator(); iterator.hasNext();) {
+            String constructorArgName = (String) iterator.next();
+            constructorArgTypes.add(attributeTypes.get(constructorArgName));
+        }
+
+        if (!constructorArgTypes.contains(null)) {
+            ConstructorMetadata constructor = classMetadata.getConstructor(new ConstructorSignature(constructorArgTypes));
+            if (constructor != null) {
+                constructor.put("always-use", "true");
+                for (ListIterator iterator = constructor.getParameters().listIterator(); iterator.hasNext();) {
+                    ParameterMetadata parameter = (ParameterMetadata) iterator.next();
+                    String name = (String) constructorArgNames.get(iterator.previousIndex());
+                    parameter.put("name", fixPropertyName(name));
+                }
+            }
+        }
+
+        if (!persistentProperties.isEmpty()) {
+            classMetadata.put("persistentProperties", persistentProperties);
+        }
+    }
+
+    private static GBeanInfo getGBeanInfo(Class type) {
+        try {
+            Method method = type.getDeclaredMethod("getGBeanInfo", new Class[]{});
+            return (GBeanInfo) method.invoke(type, new Object[]{});
+        } catch (Exception ignored) {
+        }
+
+        try {
+            Class gbeanClass = ClassLoading.loadClass(type.getName() + "GBean", type.getClassLoader());
+            Method method = gbeanClass.getDeclaredMethod("getGBeanInfo", new Class[]{});
+            return (GBeanInfo) method.invoke(gbeanClass, new Object[]{});
+        } catch (Exception ignored) {
+        }
+
+        return null;
+    }
+
+    private static String fixPropertyName(String propertyName) {
+        if (Character.isUpperCase(propertyName.charAt(0))) {
+            return Character.toLowerCase(propertyName.charAt(0)) + propertyName.substring(1);
+        }
+        return propertyName;
+    }
+}
diff --git a/kernel/src/java/org/gbean/geronimo/GeronimoServiceFactory.java b/kernel/src/java/org/gbean/geronimo/GeronimoServiceFactory.java
new file mode 100644
index 0000000..34d01a9
--- /dev/null
+++ b/kernel/src/java/org/gbean/geronimo/GeronimoServiceFactory.java
@@ -0,0 +1,359 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.geronimo;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.management.ObjectName;
+
+import org.apache.geronimo.gbean.GBeanLifecycleController;
+import org.apache.geronimo.kernel.Kernel;
+import org.gbean.metadata.ClassMetadata;
+import org.gbean.metadata.ConstructorMetadata;
+import org.gbean.metadata.MetadataManager;
+import org.gbean.metadata.ParameterMetadata;
+import org.gbean.proxy.ProxyManager;
+import org.gbean.service.ConfigurableServiceFactory;
+import org.gbean.service.ServiceContext;
+import org.gbean.spring.LifecycleDetector;
+import org.gbean.spring.NamedConstructorArgs;
+import org.gbean.spring.ServiceContextThreadLocal;
+import org.gbean.spring.SpringUtil;
+import org.springframework.beans.MutablePropertyValues;
+import org.springframework.beans.PropertyValue;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.context.support.GenericApplicationContext;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class GeronimoServiceFactory implements ConfigurableServiceFactory {
+    private final MetadataManager metadataManager;
+    private final Set persistentProperties;
+    private final ConstructorMetadata constructor;
+    private final Map dependencies;
+    private final Map dynamicProperties;
+    private final ProxyManager proxyManager;
+    private RootBeanDefinition beanDefinition;
+    private GenericApplicationContext applicationContext;
+    private Object service;
+    private boolean enabled;
+    private final List constructorArgNames;
+
+    public GeronimoServiceFactory(RootBeanDefinition beanDefinition, Map dynamicProperties, MetadataManager metadataManager, ProxyManager proxyManager) throws Exception {
+        this.metadataManager = metadataManager;
+        this.proxyManager = proxyManager;
+        this.beanDefinition = beanDefinition;
+        this.dynamicProperties = dynamicProperties;
+        dependencies = SpringUtil.extractDependencies(beanDefinition, Collections.EMPTY_MAP);
+
+        // find the constructor... geronimo always uses the same constructor
+        ClassMetadata classMetadata = metadataManager.getClassMetadata(beanDefinition.getBeanClass());
+        ConstructorMetadata constructorMetadata = null;
+        for (Iterator iterator = classMetadata.getConstructors().iterator(); iterator.hasNext();) {
+            ConstructorMetadata c = (ConstructorMetadata) iterator.next();
+            if (c.getProperties().containsKey("always-use")) {
+                constructorMetadata = c;
+                break;
+            }
+        }
+        constructor = constructorMetadata;
+
+        // determine the constructor arg names... these are alwasys defined in the gbean info
+        List constructorArgNames = new LinkedList();
+        for (Iterator iterator = constructor.getParameters().iterator(); iterator.hasNext();) {
+            ParameterMetadata parameter = (ParameterMetadata) iterator.next();
+            Object parameterName = parameter.get("name");
+            if (parameterName == null) {
+                throw new IllegalArgumentException("Parameter name is not defined");
+            }
+            constructorArgNames.add(parameterName);
+        }
+        this.constructorArgNames = Collections.unmodifiableList(constructorArgNames);
+
+        // determine the persistent properties
+        Set persistentProperties = (Set) classMetadata.get("persistentProperties");
+        if (persistentProperties == null) {
+            persistentProperties = Collections.EMPTY_SET;
+        }
+        this.persistentProperties = Collections.unmodifiableSet(persistentProperties);
+    }
+
+    public Set getPersistentProperties() {
+        return persistentProperties;
+    }
+
+    public List getConstructorArgNames() {
+        return constructorArgNames;
+    }
+
+    public Map getDynamicProperties() {
+        return dynamicProperties;
+    }
+
+    public RootBeanDefinition getBeanDefinition() {
+        RootBeanDefinition beanDefinition = new RootBeanDefinition(this.beanDefinition);
+        if (service != null) {
+            updatePersistentValues(service, beanDefinition);
+        }
+        return this.beanDefinition;
+    }
+
+    public void setBeanDefinition(RootBeanDefinition beanDefinition) {
+        this.beanDefinition = beanDefinition;
+    }
+
+    public Map getDependencies() {
+        return dependencies;
+    }
+
+    public void addDependency(String name, Set patterns) {
+        dependencies.put(name, patterns);
+    }
+
+    public Object createService(ServiceContext serviceContext) throws Exception {
+        try {
+            Object service = null;
+            ServiceContext oldServiceContext = ServiceContextThreadLocal.get();
+            ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
+            try {
+                ServiceContextThreadLocal.set(serviceContext);
+                Thread.currentThread().setContextClassLoader(serviceContext.getClassLoader());
+
+                // dereference all factories
+                MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
+                PropertyValue[] values = propertyValues.getPropertyValues();
+                for (int i = 0; i < values.length; i++) {
+                    PropertyValue propertyValue = values[i];
+                    if (propertyValue.getValue() instanceof FactoryBean) {
+                        FactoryBean factoryBean = (FactoryBean) propertyValue.getValue();
+                        Object object = factoryBean.getObject();
+                        propertyValues.removePropertyValue(propertyValue.getName());
+                        propertyValues.addPropertyValue(propertyValue.getName(), object);
+                    }
+                }
+
+                applicationContext = new GenericApplicationContext();
+
+                // register the post processors
+                NamedConstructorArgs namedConstructorArgs = new NamedConstructorArgs(metadataManager);
+                namedConstructorArgs.addDefaultValue("objectName", String.class, serviceContext.getObjectName());
+                namedConstructorArgs.addDefaultValue("objectName", ObjectName.class, new ObjectName(serviceContext.getObjectName()));
+                namedConstructorArgs.addDefaultValue("classLoader", ClassLoader.class, serviceContext.getClassLoader());
+                namedConstructorArgs.addDefaultValue("gbeanLifecycleController", GBeanLifecycleController.class, new GeronimoLifecycleControllerReference().getObject());
+                namedConstructorArgs.addDefaultValue("kernel", Kernel.class, serviceContext.getKernel().getService(Kernel.KERNEL));
+
+                applicationContext.addBeanFactoryPostProcessor(namedConstructorArgs);
+                LifecycleDetector lifecycleDetector = new LifecycleDetector();
+                lifecycleDetector.addLifecycleInterface(org.apache.geronimo.gbean.GBeanLifecycle.class, "doStart", "doStop");
+                applicationContext.addBeanFactoryPostProcessor(lifecycleDetector);
+                applicationContext.getBeanFactory().addBeanPostProcessor(new DynamicGBeanProcessor(serviceContext.getObjectName(), dynamicProperties));
+
+                // copy the bean definition, so we don't modify the original value
+                RootBeanDefinition beanDefinition = new RootBeanDefinition(this.beanDefinition);
+
+                // build the bean
+                applicationContext.registerBeanDefinition(serviceContext.getObjectName(), beanDefinition);
+                applicationContext.refresh();
+                service = applicationContext.getBean(serviceContext.getObjectName());
+            } finally {
+                ServiceContextThreadLocal.set(oldServiceContext);
+                Thread.currentThread().setContextClassLoader(oldClassLoader);
+            }
+            this.service = service;
+            return service;
+        } catch (Throwable t) {
+            applicationContext = null;
+
+            if (t instanceof Exception) {
+                throw (Exception) t;
+            } else if (t instanceof Error) {
+                throw (Error) t;
+            } else {
+                throw new Error(t);
+            }
+        }
+    }
+
+    public void destroyService(ServiceContext serviceContext, Object service) {
+        // update the persistent attributes
+        try {
+            if (service != null) {
+                // update the persistent values
+                RootBeanDefinition beanDefinition = new RootBeanDefinition(this.beanDefinition);
+                updatePersistentValues(service, beanDefinition);
+                this.beanDefinition = beanDefinition;
+            }
+        } finally {
+            this.service = null;
+            if (applicationContext != null) {
+                applicationContext.close();
+                applicationContext = null;
+            }
+        }
+    }
+
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    public Set getPropertyNames() {
+        if (persistentProperties != null) {
+            return persistentProperties;
+        } else {
+            return Collections.EMPTY_SET;
+        }
+    }
+
+    public Object getProperty(String propertyName) {
+        if (persistentProperties == null || !persistentProperties.contains(propertyName)) {
+            throw new IllegalArgumentException("Property is not persistent:" +
+                    " propertyName=" + propertyName +
+                    ", serviceType: " + beanDefinition.getBeanClassName());
+        }
+
+        PropertyValue propertyValue = beanDefinition.getPropertyValues().getPropertyValue(propertyName);
+        if (propertyValue != null) {
+            return propertyValue.getValue();
+        }
+
+        if (dynamicProperties.containsKey(propertyName)) {
+            return dynamicProperties.get(propertyValue);
+        }
+
+        return null;
+    }
+
+    public void setProperty(String propertyName, Object persistentValue) {
+        if (persistentProperties == null || !persistentProperties.contains(propertyName)) {
+            throw new IllegalArgumentException("Property is not persistent:" +
+                    " propertyName=" + propertyName +
+                    ", serviceType: " + beanDefinition.getBeanClassName());
+        }
+
+        if (dynamicProperties.containsKey(propertyName)) {
+            dynamicProperties.put(propertyName, persistentValue);
+            return;
+        }
+
+        beanDefinition.getPropertyValues().removePropertyValue(propertyName);
+        beanDefinition.getPropertyValues().addPropertyValue(propertyName, persistentValue);
+    }
+
+    private void updatePersistentValues(Object service, RootBeanDefinition beanDefinition) {
+        Map getters = new HashMap();
+        Method[] methods = service.getClass().getMethods();
+        for (int i = 0; i < methods.length; i++) {
+            Method method = methods[i];
+            String methodName = method.getName();
+            if (Modifier.isPublic(method.getModifiers()) && !Modifier.isStatic(method.getModifiers())) {
+                if (method.getParameterTypes().length == 0 && method.getReturnType() != Void.TYPE) {
+                    if (methodName.length() > 3 && methodName.startsWith("get") && !methodName.equals("getClass")) {
+                        String propertyName = fixPropertyName(methodName.substring(3));
+                        getters.put(propertyName, method);
+                    } else if (methodName.length() > 2 && methodName.startsWith("is")) {
+                        String propertyName = fixPropertyName(methodName.substring(2));
+                        getters.put(propertyName, method);
+                    }
+                }
+            }
+        }
+
+        for (Iterator iterator = persistentProperties.iterator(); iterator.hasNext();) {
+            String propertyName = (String) iterator.next();
+
+            if (dynamicProperties.containsKey(propertyName)) {
+                Object value = getCurrentValue(getters, service, propertyName, dynamicProperties.get(propertyName));
+                dynamicProperties.put(propertyName, value);
+            } else {
+                // get the new property value
+                Object value = null;
+                PropertyValue propertyValue = beanDefinition.getPropertyValues().getPropertyValue(propertyName);
+                if (propertyValue != null) {
+                    value = propertyValue.getValue();
+                }
+                value = getCurrentValue(getters, service, propertyName, value);
+
+                // update the property value
+                beanDefinition.getPropertyValues().removePropertyValue(propertyName);
+                if (value != null) {
+                    beanDefinition.getPropertyValues().addPropertyValue(propertyName, value);
+                }
+            }
+        }
+    }
+
+    private Object getCurrentValue(Map getters, Object service, String propertyName, Object defaultValue) {
+        Object value = defaultValue;
+        try {
+            Method getter = (Method) getters.get(propertyName);
+            if (getter != null) {
+                value = getter.invoke(service, null);
+            }
+        } catch (Throwable throwable) {
+            while (throwable instanceof InvocationTargetException) {
+                throwable = ((InvocationTargetException) throwable).getTargetException();
+            }
+
+            throw new RuntimeException("Problem while obtaining the currennt persistent value of property: " +
+                    "propertyName=" + propertyName +
+                    ", serviceType: " + beanDefinition.getBeanClassName(),
+                    throwable);
+        }
+
+        // we should never get a bean definition holder
+        // we don't support them sice it is not serizlizable
+        if (value instanceof BeanDefinitionHolder) {
+            throw new IllegalArgumentException("Got a bean definition holder");
+        }
+
+        // turn proxies back into the factory bean
+        // the factory bean is serizlizable
+        if (value instanceof FactoryBeanProvider) {
+            value = ((FactoryBeanProvider) value).getFactoryBean();
+        } else {
+            Object proxyData = proxyManager.getProxyData(value);
+            if (proxyData instanceof FactoryBeanProvider) {
+                value = ((FactoryBeanProvider) proxyData).getFactoryBean();
+            } else if (proxyData instanceof FactoryBean) {
+                value = proxyData;
+            }
+        }
+        return value;
+    }
+
+    private static String fixPropertyName(String propertyName) {
+        if (Character.isUpperCase(propertyName.charAt(0))) {
+            return Character.toLowerCase(propertyName.charAt(0)) + propertyName.substring(1);
+        }
+        return propertyName;
+    }
+}
diff --git a/kernel/src/java/org/gbean/geronimo/GeronimoUtil.java b/kernel/src/java/org/gbean/geronimo/GeronimoUtil.java
new file mode 100644
index 0000000..610dac9
--- /dev/null
+++ b/kernel/src/java/org/gbean/geronimo/GeronimoUtil.java
@@ -0,0 +1,311 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.geronimo;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.management.ObjectName;
+
+import org.apache.geronimo.gbean.DynamicGAttributeInfo;
+import org.apache.geronimo.gbean.GAttributeInfo;
+import org.apache.geronimo.gbean.GBeanData;
+import org.apache.geronimo.gbean.GBeanInfo;
+import org.apache.geronimo.gbean.GConstructorInfo;
+import org.apache.geronimo.gbean.GReferenceInfo;
+import org.apache.geronimo.gbean.InvalidConfigurationException;
+import org.gbean.metadata.MetadataManager;
+import org.gbean.proxy.ProxyManager;
+import org.springframework.beans.MutablePropertyValues;
+import org.springframework.beans.PropertyValue;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class GeronimoUtil {
+    /**
+     * Converts the GeronimoServiceFactory into a geronimo GBeanData
+     *
+     * @return the gbean data
+     */
+    public static GBeanData createGBeanData(ObjectName objectName, GeronimoServiceFactory serviceFactory) {
+        RootBeanDefinition beanDefinition = serviceFactory.getBeanDefinition();
+        Set persistentProperties = serviceFactory.getPersistentProperties();
+        GBeanData gbeanData = new GBeanData(objectName, createGBeanInfo(serviceFactory));
+        gbeanData.setAttribute("gbeanEnabled", Boolean.valueOf(serviceFactory.isEnabled()));
+
+        // add the normal properties
+        PropertyValue[] properties = beanDefinition.getPropertyValues().getPropertyValues();
+        for (int i = 0; i < properties.length; i++) {
+            PropertyValue propertyValue = properties[i];
+            if (persistentProperties.contains(propertyValue.getName())) {
+                gbeanData.setAttribute(propertyValue.getName(), propertyValue.getValue());
+            }
+        }
+
+        // add the dynamic properties
+        for (Iterator iterator = serviceFactory.getDynamicProperties().entrySet().iterator(); iterator.hasNext();) {
+            Map.Entry entry = (Map.Entry) iterator.next();
+            String propertyName = (String) entry.getKey();
+            Object propertyValue = entry.getValue();
+            gbeanData.setAttribute(propertyName, propertyValue);
+        }
+
+        return gbeanData;
+    }
+
+    /**
+     * Converts the GeronimoServiceFactory into a geronimo GBeanInfo
+     *
+     * @return the GBeanInfo for the service
+     */
+    public static GBeanInfo createGBeanInfo(GeronimoServiceFactory serviceFactory) {
+        RootBeanDefinition beanDefinition = serviceFactory.getBeanDefinition();
+        Set persistentProperties = serviceFactory.getPersistentProperties();
+
+        // add the normal properties
+        Set attributeInfos = new HashSet();
+        PropertyValue[] properties = beanDefinition.getPropertyValues().getPropertyValues();
+        for (int i = 0; i < properties.length; i++) {
+            PropertyValue propertyValue = properties[i];
+            String propertyName = propertyValue.getName();
+            attributeInfos.add(new GAttributeInfo(propertyName,
+                    "java.lang.Object",
+                    persistentProperties.contains(propertyName),
+                    null,
+                    null));
+        }
+
+        // add the dynamic properties
+        for (Iterator iterator = serviceFactory.getDynamicProperties().keySet().iterator(); iterator.hasNext();) {
+            String propertyName = (String) iterator.next();
+            attributeInfos.add(new DynamicGAttributeInfo(propertyName,
+                    "java.lang.Object",
+                    true,
+                    true,
+                    true));
+        }
+
+        return new GBeanInfo(beanDefinition.getBeanClassName(),
+                "GBean",
+                attributeInfos,
+                new GConstructorInfo(serviceFactory.getConstructorArgNames()),
+                Collections.EMPTY_SET,
+                null);
+    }
+
+    public static GeronimoServiceFactory createGeronimoServiceFactory(GBeanData gbeanData, ClassLoader classLoader, MetadataManager metadataManager, ProxyManager proxyManager) throws Exception {
+        ObjectName objectName = gbeanData.getName();
+        String beanClassName = gbeanData.getGBeanInfo().getClassName();
+
+        // get a list of all methods in the target class
+        Class beanClass = null;
+        try {
+            beanClass = classLoader.loadClass(beanClassName);
+        } catch (ClassNotFoundException e) {
+            throw new InvalidConfigurationException("Could not load class for GBeanInstance: objectName=" + objectName + " className=" + beanClassName);
+        }
+        Method[] methods = beanClass.getMethods();
+
+        // build a map of the property names in lower case to the real property name
+        // this fixes problem where geronimo property names were loosly matched
+        Map lowerCasePropertyNameMap = new HashMap(methods.length);
+        for (int i = 0; i < methods.length; i++) {
+            Method method = methods[i];
+            String methodName = method.getName();
+            if (Modifier.isPublic(method.getModifiers()) && !Modifier.isStatic(method.getModifiers())) {
+                if (method.getParameterTypes().length == 0 && method.getReturnType() != Void.TYPE) {
+                    if (methodName.length() > 3 && methodName.startsWith("get") && !methodName.equals("getClass")) {
+                        String propertyName = fixPropertyName(methodName.substring(3));
+                        lowerCasePropertyNameMap.put(propertyName.toLowerCase(), propertyName);
+                    } else if (methodName.length() > 2 && methodName.startsWith("is")) {
+                        String propertyName = fixPropertyName(methodName.substring(2));
+                        lowerCasePropertyNameMap.put(propertyName.toLowerCase(), propertyName);
+                    }
+                }
+                if (method.getParameterTypes().length == 1 &&
+                        method.getReturnType() == Void.TYPE &&
+                        methodName.length() > 3 &&
+                        methodName.startsWith("set")) {
+                    String propertyName = fixPropertyName(methodName.substring(3));
+                    lowerCasePropertyNameMap.put(propertyName.toLowerCase(), propertyName);
+                }
+            }
+        }
+
+        // build a list of constructor args... adjust any incoming names to match the property name as determined by the getters and setters
+        List constructorArgs = new ArrayList();
+        for (Iterator iterator = gbeanData.getGBeanInfo().getConstructor().getAttributeNames().iterator(); iterator.hasNext();) {
+            // for constructor args we only use simple fixPropertyName method
+            String argName = fixPropertyName((String) iterator.next());
+            constructorArgs.add(argName);
+            lowerCasePropertyNameMap.put(argName.toLowerCase(), argName);
+        }
+
+        // determine the types of all properties... these are needed for constructor args
+        Map persistentTypes = new HashMap();
+        for (Iterator iterator = gbeanData.getGBeanInfo().getAttributes().iterator(); iterator.hasNext();) {
+            GAttributeInfo attributeInfo = (GAttributeInfo) iterator.next();
+            String propertyName = attributeInfo.getName();
+            if (!(attributeInfo instanceof DynamicGAttributeInfo)) {
+                propertyName = fixPropertyName(attributeInfo.getName());
+                if (lowerCasePropertyNameMap.containsKey(propertyName.toLowerCase())) {
+                    propertyName = (String) lowerCasePropertyNameMap.get(propertyName.toLowerCase());
+                }
+            }
+            persistentTypes.put(propertyName, attributeInfo.getType());
+        }
+        for (Iterator iterator = gbeanData.getGBeanInfo().getReferences().iterator(); iterator.hasNext();) {
+            GReferenceInfo referenceInfo = (GReferenceInfo) iterator.next();
+            String propertyName = fixPropertyName(referenceInfo.getName());
+            if (lowerCasePropertyNameMap.containsKey(propertyName.toLowerCase())) {
+                propertyName = (String) lowerCasePropertyNameMap.get(propertyName.toLowerCase());
+            }
+            persistentTypes.put(propertyName, referenceInfo.getProxyType());
+        }
+
+        // determine which properties are dynamic
+        Map dynamicProperties = new HashMap();
+        for (Iterator iterator = gbeanData.getGBeanInfo().getAttributes().iterator(); iterator.hasNext();) {
+            GAttributeInfo attributeInfo = (GAttributeInfo) iterator.next();
+            if (attributeInfo instanceof DynamicGAttributeInfo) {
+                dynamicProperties.put(attributeInfo.getName(), null);
+            }
+        }
+
+        // values from the properties
+        MutablePropertyValues propertyValues = new MutablePropertyValues();
+        for (Iterator iterator = gbeanData.getGBeanInfo().getAttributes().iterator(); iterator.hasNext();) {
+            GAttributeInfo attributeInfo = (GAttributeInfo) iterator.next();
+            String propertyName = attributeInfo.getName();
+
+            // skip the gbeanEnabled property... it is handled below
+            if (propertyName.equals("gbeanEnabled")) {
+                continue;
+            }
+
+            // fix any non-dynamic property name so that it matches spring's rules
+            if (!dynamicProperties.containsKey(propertyName)) {
+                propertyName = fixPropertyName(propertyName);
+                if (lowerCasePropertyNameMap.containsKey(propertyName.toLowerCase())) {
+                    propertyName = (String) lowerCasePropertyNameMap.get(propertyName.toLowerCase());
+                }
+            }
+
+//            String propertyType = (String) persistentTypes.get(propertyName);
+            Object propertyValue = gbeanData.getAttribute(attributeInfo.getName());
+
+            // magic attributes
+//            if (attributeInfo.isWritable() || constructorArgs.contains(propertyName)) {
+//                if (propertyName.equals("objectName") && propertyType.equals(String.class.getName())) {
+//                    propertyValue = new ObjectNameStringReference();
+//                } else if (propertyName.equals("objectName") && propertyType.equals(ObjectName.class.getName())) {
+//                    propertyValue = new ObjectNameReference();
+//                } else if (propertyName.equals("classLoader") && propertyType.equals(ClassLoader.class.getName())) {
+//                    propertyValue = new ClassLoaderReference();
+//                } else if (propertyName.equals("gbeanLifecycleController") && propertyType.equals(GBeanLifecycleController.class.getName())) {
+//                    propertyValue = new GeronimoLifecycleControllerReference();
+//                } else if (propertyName.equals("kernel") && propertyType.equals(Kernel.class.getName())) {
+//                    propertyValue = new GeronimoKernelReference();
+//                }
+//            }
+
+            // set the property... we only set properties that have a defined value
+            if (propertyValue != null || gbeanData.getAttributes().containsKey(attributeInfo.getName())) {
+                if (dynamicProperties.containsKey(propertyName)) {
+                    dynamicProperties.put(propertyName, propertyValue);
+                } else {
+                    propertyValues.addPropertyValue(propertyName, propertyValue);
+                }
+            }
+        }
+
+        // values from the references
+        //
+        // NOTE: we need to add the reference values after the attribue values so that newly added
+        // reference patterns will overwrite references stored in properties in a previous run
+        for (Iterator iterator = gbeanData.getGBeanInfo().getReferences().iterator(); iterator.hasNext();) {
+            GReferenceInfo referenceInfo = (GReferenceInfo) iterator.next();
+            String propertyName = referenceInfo.getName();
+
+            // get the patterns before we mess with the proptery name
+            Set patterns = gbeanData.getReferencePatterns(propertyName);
+
+            // fix any non-dynamic property name so that it matches spring's rules
+            if (!dynamicProperties.containsKey(propertyName)) {
+                propertyName = fixPropertyName(propertyName);
+                if (lowerCasePropertyNameMap.containsKey(propertyName.toLowerCase())) {
+                    propertyName = (String) lowerCasePropertyNameMap.get(propertyName.toLowerCase());
+                }
+            }
+
+            // get the patterns
+            if (patterns != null && !patterns.isEmpty()) {
+                // Remove all nulls from the patterns... there is bad code out there
+                patterns = new HashSet(patterns);
+                for (Iterator patternIterator = patterns.iterator(); patternIterator.hasNext();) {
+                    Object pattern = patternIterator.next();
+                    if (pattern == null) {
+                        patternIterator.remove();
+                    }
+                }
+            }
+
+            // set the property... we only set properties that have a defined value
+            if (patterns != null && !patterns.isEmpty()) {
+                Object propertyValue;
+                if (referenceInfo.getProxyType().equals(Collection.class.getName())) {
+                    propertyValue = new CollectionReference(propertyName, patterns, referenceInfo.getReferenceType());
+                } else {
+                    propertyValue = new SingletonReference(propertyName, patterns, referenceInfo.getReferenceType());
+                }
+                if (dynamicProperties.containsKey(propertyName)) {
+                    dynamicProperties.put(propertyName, propertyValue);
+                } else {
+                    propertyValues.addPropertyValue(propertyName, propertyValue);
+                }
+            }
+        }
+
+        RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass, propertyValues);
+        GeronimoServiceFactory geronimoServiceFactory = new GeronimoServiceFactory(beanDefinition, dynamicProperties, metadataManager, proxyManager);
+
+        boolean enabled = true;
+        if (gbeanData.getAttributes().containsKey("gbeanEnabled")) {
+            enabled = ((Boolean) gbeanData.getAttribute("gbeanEnabled")).booleanValue();
+        }
+        geronimoServiceFactory.setEnabled(enabled);
+
+        return geronimoServiceFactory;
+    }
+
+    private static String fixPropertyName(String propertyName) {
+        if (Character.isUpperCase(propertyName.charAt(0))) {
+            return Character.toLowerCase(propertyName.charAt(0)) + propertyName.substring(1);
+        }
+        return propertyName;
+    }
+}
diff --git a/kernel/src/java/org/gbean/geronimo/KernelBridge.java b/kernel/src/java/org/gbean/geronimo/KernelBridge.java
new file mode 100644
index 0000000..811dc0c
--- /dev/null
+++ b/kernel/src/java/org/gbean/geronimo/KernelBridge.java
@@ -0,0 +1,467 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.gbean.geronimo;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import javax.management.ObjectName;
+
+import org.gbean.kernel.Kernel;
+import org.gbean.kernel.NoSuchAttributeException;
+import org.gbean.kernel.NoSuchOperationException;
+import org.gbean.kernel.ServiceAlreadyExistsException;
+import org.gbean.kernel.ServiceNotFoundException;
+import org.gbean.kernel.runtime.ServiceState;
+import org.gbean.kernel.simple.SimpleKernel;
+import org.gbean.metadata.MetadataManager;
+import org.gbean.proxy.ProxyManager;
+import org.gbean.reflect.PropertyInvoker;
+import org.gbean.reflect.ServiceInvoker;
+import org.gbean.reflect.ServiceInvokerManager;
+import org.gbean.service.ConfigurableServiceFactory;
+import org.gbean.service.ServiceFactory;
+
+
+/**
+ * @version $Rev: 154947 $ $Date: 2005-02-22 20:10:45 -0800 (Tue, 22 Feb 2005) $
+ */
+public class KernelBridge implements org.apache.geronimo.kernel.Kernel {
+    /**
+     * Helper objects for invoke and getAttribute
+     */
+    private static final Object[] NO_ARGS = new Object[0];
+    private static final String[] NO_TYPES = new String[0];
+
+    private final SimpleKernel kernel;
+    private final DependencyManagerBridge dependencyManagerBridge;
+    private final MetadataManager metadataManager;
+    private final ServiceInvokerManager serviceInvokerManager;
+    private final ProxyManager proxyManager;
+    private final ProxyManagerBridge proxyManagerBridge;
+    private final LifecycleMonitorBridge lifecycleMonitorBridge;
+
+    public KernelBridge(SimpleKernel kernel, MetadataManager metadataManager, ServiceInvokerManager serviceInvokerManager, ProxyManager proxyManager) {
+        this.kernel = kernel;
+        this.dependencyManagerBridge = new DependencyManagerBridge(kernel.getDependencyManager());
+        this.metadataManager = metadataManager;
+        this.serviceInvokerManager = serviceInvokerManager;
+        this.proxyManager = proxyManager;
+        this.lifecycleMonitorBridge = new LifecycleMonitorBridge(kernel);
+        this.proxyManagerBridge = new ProxyManagerBridge(proxyManager);
+        System.setProperty("geronimo.base.dir", System.getProperty("gbean.base.dir"));
+    }
+
+    public Kernel getKernel() {
+        return kernel;
+    }
+
+    public String getKernelName() {
+        return kernel.getKernelName();
+    }
+
+    public org.apache.geronimo.kernel.DependencyManager getDependencyManager() {
+        return dependencyManagerBridge;
+    }
+
+    public org.apache.geronimo.kernel.lifecycle.LifecycleMonitor getLifecycleMonitor() {
+        return lifecycleMonitorBridge;
+    }
+
+    public org.apache.geronimo.kernel.proxy.ProxyManager getProxyManager() {
+        return proxyManagerBridge;
+    }
+
+    public Object getAttribute(ObjectName objectName, String attributeName)
+            throws org.apache.geronimo.kernel.GBeanNotFoundException,
+            org.apache.geronimo.kernel.NoSuchAttributeException,
+            Exception {
+
+        boolean running = isRunning(objectName);
+        if (running) {
+            ServiceInvoker serviceInvoker = getServiceInvoker(objectName);
+            try {
+                Object value = serviceInvoker.getAttribute(attributeName);
+                return value;
+            } catch (NoSuchAttributeException e) {
+                throw new org.apache.geronimo.kernel.NoSuchAttributeException(e);
+            }
+        } else {
+            ServiceFactory serviceFactory = getServiceFactory(objectName);
+            if (!(serviceFactory instanceof ConfigurableServiceFactory)) {
+                throw new NoSuchAttributeException("Service is stopped and the service factory not configurable: objectName=" + objectName + ", propertyName=" + attributeName);
+            }
+            ConfigurableServiceFactory configurableServiceFactory = (ConfigurableServiceFactory) serviceFactory;
+            return configurableServiceFactory.getProperty(attributeName);
+        }
+    }
+
+    public void setAttribute(ObjectName objectName, String attributeName, Object attributeValue)
+            throws org.apache.geronimo.kernel.GBeanNotFoundException,
+            org.apache.geronimo.kernel.NoSuchAttributeException,
+            Exception {
+
+        boolean running = isRunning(objectName);
+        if (running) {
+            ServiceInvoker serviceInvoker = getServiceInvoker(objectName);
+            try {
+                serviceInvoker.setAttribute(attributeName, attributeValue);
+            } catch (NoSuchAttributeException e) {
+                throw new org.apache.geronimo.kernel.NoSuchAttributeException(e);
+            }
+        } else {
+            ServiceFactory serviceFactory = getServiceFactory(objectName);
+            if (!(serviceFactory instanceof ConfigurableServiceFactory)) {
+                throw new NoSuchAttributeException("Service is stopped and the service factory not configurable: objectName=" + objectName + ", propertyName=" + attributeName);
+            }
+            ConfigurableServiceFactory configurableServiceFactory = (ConfigurableServiceFactory) serviceFactory;
+            configurableServiceFactory.setProperty(attributeName, attributeValue);
+        }
+    }
+
+    public Object invoke(ObjectName objectName, String methodName)
+            throws org.apache.geronimo.kernel.GBeanNotFoundException,
+            org.apache.geronimo.kernel.NoSuchOperationException,
+            Exception {
+
+        boolean running = isRunning(objectName);
+        if (!running) {
+            throw new IllegalStateException("Service is not running: name=" + objectName);
+        }
+
+        ServiceInvoker serviceInvoker = getServiceInvoker(objectName);
+        try {
+            Object value = serviceInvoker.invoke(methodName, NO_ARGS, NO_TYPES);
+            return value;
+        } catch (NoSuchOperationException e) {
+            throw new org.apache.geronimo.kernel.NoSuchOperationException(e);
+        }
+    }
+
+    public Object invoke(ObjectName objectName, String methodName, Object[] args, String[] types) throws org.apache.geronimo.kernel.GBeanNotFoundException,
+            org.apache.geronimo.kernel.NoSuchOperationException,
+            Exception {
+
+        ServiceInvoker serviceInvoker = getServiceInvoker(objectName);
+        try {
+            Object value = serviceInvoker.invoke(methodName, args, types);
+            return value;
+        } catch (NoSuchOperationException e) {
+            throw new org.apache.geronimo.kernel.NoSuchOperationException(e);
+        }
+    }
+
+    public boolean isLoaded(ObjectName name) {
+        return kernel.isLoaded(name);
+    }
+
+    public org.apache.geronimo.gbean.GBeanInfo getGBeanInfo(ObjectName name) throws org.apache.geronimo.kernel.GBeanNotFoundException {
+        ServiceFactory serviceFactory = getServiceFactory(name);
+        if (serviceFactory instanceof GeronimoServiceFactory) {
+            GeronimoServiceFactory geronimoServiceFactory = (GeronimoServiceFactory) serviceFactory;
+            return GeronimoUtil.createGBeanInfo(geronimoServiceFactory);
+        }
+
+        return createGBeanInfo(name);
+    }
+
+    public org.apache.geronimo.gbean.GBeanData getGBeanData(ObjectName objectName) throws org.apache.geronimo.kernel.GBeanNotFoundException{
+        ServiceFactory serviceFactory = getServiceFactory(objectName);
+        if (serviceFactory instanceof GeronimoServiceFactory) {
+            GeronimoServiceFactory geronimoServiceFactory = null;
+            geronimoServiceFactory = (GeronimoServiceFactory) serviceFactory;
+            return GeronimoUtil.createGBeanData(objectName, geronimoServiceFactory);
+        }
+
+        return createGBeanData(objectName);
+    }
+
+    public void loadGBean(org.apache.geronimo.gbean.GBeanData gbeanData, ClassLoader classLoader) throws org.apache.geronimo.kernel.GBeanAlreadyExistsException {
+        ObjectName objectName = gbeanData.getName();
+
+        GeronimoServiceFactory geronimoServiceFactory = null;
+        try {
+            geronimoServiceFactory =GeronimoUtil.createGeronimoServiceFactory(gbeanData, classLoader, metadataManager, proxyManager);
+        } catch (Exception e) {
+            throw new org.apache.geronimo.kernel.InternalKernelException(e);
+        }
+        try {
+            kernel.loadService(objectName, geronimoServiceFactory, classLoader);
+        } catch (ServiceAlreadyExistsException e) {
+            throw new org.apache.geronimo.kernel.GBeanAlreadyExistsException(e);
+        }
+    }
+
+    public void startGBean(ObjectName name) throws org.apache.geronimo.kernel.GBeanNotFoundException,  IllegalStateException {
+        try {
+            kernel.startService(name);
+        } catch (ServiceNotFoundException e) {
+            throw new org.apache.geronimo.kernel.GBeanNotFoundException(e);
+        }
+    }
+
+    public void startRecursiveGBean(ObjectName name) throws org.apache.geronimo.kernel.GBeanNotFoundException, IllegalStateException {
+        try {
+            kernel.startRecursiveService(name);
+        } catch (ServiceNotFoundException e) {
+            throw new org.apache.geronimo.kernel.GBeanNotFoundException(e);
+        }
+    }
+
+    public void stopGBean(ObjectName name) throws org.apache.geronimo.kernel.GBeanNotFoundException, IllegalStateException {
+        try {
+            kernel.stopService(name);
+        } catch (ServiceNotFoundException e) {
+            throw new org.apache.geronimo.kernel.GBeanNotFoundException(e);
+        }
+    }
+
+    public void unloadGBean(ObjectName name) throws org.apache.geronimo.kernel.GBeanNotFoundException, IllegalStateException {
+        try {
+            kernel.unloadService(name);
+        } catch (ServiceNotFoundException e) {
+            throw new org.apache.geronimo.kernel.GBeanNotFoundException(e);
+        }
+    }
+    
+    public int getGBeanState(ObjectName name) throws org.apache.geronimo.kernel.GBeanNotFoundException {
+        try {
+            return kernel.getServiceState(name);
+        } catch (ServiceNotFoundException e) {
+            throw new org.apache.geronimo.kernel.GBeanNotFoundException(e);
+        }
+    }
+
+    public long getGBeanStartTime(ObjectName name) throws org.apache.geronimo.kernel.GBeanNotFoundException {
+        try {
+            return kernel.getServiceStartTime(name);
+        } catch (ServiceNotFoundException e) {
+            throw new org.apache.geronimo.kernel.GBeanNotFoundException(e);
+        }
+    }
+
+    public boolean isGBeanEnabled(ObjectName name) throws org.apache.geronimo.kernel.GBeanNotFoundException {
+        try {
+            return kernel.isServiceEnabled(name);
+        } catch (ServiceNotFoundException e) {
+            throw new org.apache.geronimo.kernel.GBeanNotFoundException(e);
+        }
+    }
+
+    public void setGBeanEnabled(ObjectName name, boolean enabled) throws org.apache.geronimo.kernel.GBeanNotFoundException {
+        try {
+            kernel.setServiceEnabled(name, enabled);
+        } catch (ServiceNotFoundException e) {
+            throw new org.apache.geronimo.kernel.GBeanNotFoundException(e);
+        }
+    }
+
+    public Set listGBeans(ObjectName pattern) {
+        return kernel.listServiceNames(pattern);
+    }
+
+    public Set listGBeans(Set patterns) {
+        return kernel.listServiceNames(patterns);
+    }
+
+    public void boot() throws Exception {
+        org.apache.geronimo.kernel.KernelRegistry.registerKernel(this);
+        kernel.registerShutdownHook(new Runnable() {
+            public void run() {
+                synchronized (KernelBridge.this) {
+                    KernelBridge.this.notify();
+                }
+                org.apache.geronimo.kernel.KernelRegistry.unregisterKernel(KernelBridge.this);
+            }
+        });
+    }
+
+    public Date getBootTime() {
+        return kernel.getBootTime();
+    }
+
+    public void registerShutdownHook(Runnable hook) {
+        kernel.registerShutdownHook(hook);
+    }
+
+    public void unregisterShutdownHook(Runnable hook) {
+        kernel.unregisterShutdownHook(hook);
+    }
+
+    public void shutdown() {
+        kernel.shutdown();
+    }
+
+    public boolean isRunning() {
+        return kernel.isRunning();
+    }
+
+    public ClassLoader getClassLoaderFor(ObjectName name) throws org.apache.geronimo.kernel.GBeanNotFoundException {
+        try {
+            return kernel.getClassLoaderFor(name);
+        } catch (ServiceNotFoundException e) {
+            throw new org.apache.geronimo.kernel.GBeanNotFoundException(e);
+        }
+    }
+
+    private ServiceInvoker getServiceInvoker(ObjectName objectName) throws org.apache.geronimo.kernel.GBeanNotFoundException {
+        try {
+            return serviceInvokerManager.getServiceInvoker(objectName);
+        } catch (ServiceNotFoundException e) {
+            throw new org.apache.geronimo.kernel.GBeanNotFoundException(e);
+        }
+    }
+
+    private ServiceFactory getServiceFactory(ObjectName objectName) throws org.apache.geronimo.kernel.GBeanNotFoundException {
+        try {
+            return kernel.getServiceFactory(objectName);
+        } catch (ServiceNotFoundException e) {
+            throw new org.apache.geronimo.kernel.GBeanNotFoundException(e);
+        }
+    }
+    private boolean isRunning(ObjectName objectName) {
+        try {
+            int serviceState = kernel.getServiceState(objectName);
+            boolean running = serviceState == ServiceState.RUNNING_INDEX || serviceState == ServiceState.STOPPING_INDEX;
+            return running;
+        } catch (ServiceNotFoundException e) {
+            return false;
+        }
+    }
+
+    private org.apache.geronimo.gbean.GBeanInfo createGBeanInfo(ObjectName objectName) throws org.apache.geronimo.kernel.GBeanNotFoundException {
+        boolean running = isRunning(objectName);
+        if (running) {
+            ServiceInvoker serviceInvoker = getServiceInvoker(objectName);
+            return createGBeanInfo(serviceInvoker);
+        } else {
+            ServiceFactory serviceFactory = getServiceFactory(objectName);
+            return createGBeanInfo(serviceFactory);
+        }
+
+    }
+
+    private org.apache.geronimo.gbean.GBeanInfo createGBeanInfo(ServiceInvoker serviceInvoker) {
+        Set attributeInfos = new HashSet();
+        List propertyIndex = serviceInvoker.getPropertyIndex();
+        for (Iterator iterator = propertyIndex.iterator(); iterator.hasNext();) {
+            PropertyInvoker propertyInvoker = (PropertyInvoker) iterator.next();
+            String getterName = null;
+            if (propertyInvoker.isReadable()) {
+                getterName = propertyInvoker.getGetterSignature().getName();
+            }
+            String setterName = null;
+            if (propertyInvoker.isWritable()) {
+                setterName = propertyInvoker.getSetterSignature().getName();
+            }
+            attributeInfos.add(new org.apache.geronimo.gbean.GAttributeInfo(propertyInvoker.getPropertyName(),
+                    propertyInvoker.getType().getName(),
+                    false,
+                    getterName,
+                    setterName));
+        }
+        return new org.apache.geronimo.gbean.GBeanInfo(serviceInvoker.getServiceType().getName(),
+                "GBean",
+                attributeInfos,
+                new org.apache.geronimo.gbean.GConstructorInfo(new String[] {}),
+                Collections.EMPTY_SET,
+                Collections.EMPTY_SET);
+    }
+
+    private org.apache.geronimo.gbean.GBeanInfo createGBeanInfo(ServiceFactory serviceFactory) {
+        String serviceType;
+        Set attributeInfos = new HashSet();
+        serviceType = Object.class.getName();
+        if (serviceFactory instanceof ConfigurableServiceFactory) {
+            ConfigurableServiceFactory configurableServiceFactory = (ConfigurableServiceFactory) serviceFactory;
+            Set propertyNames = configurableServiceFactory.getPropertyNames();
+            for (Iterator iterator = propertyNames.iterator(); iterator.hasNext();) {
+                String propertyName = (String) iterator.next();
+                String ucase = Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
+                String getterName = "get" + ucase;
+                String setterName = "set" + ucase;
+                attributeInfos.add(new org.apache.geronimo.gbean.GAttributeInfo(propertyName,
+                        Object.class.getName(),
+                        false,
+                        getterName,
+                        setterName));
+            }
+        }
+        return new org.apache.geronimo.gbean.GBeanInfo(serviceType,
+                "GBean",
+                attributeInfos,
+                new org.apache.geronimo.gbean.GConstructorInfo(new String[] {}),
+                Collections.EMPTY_SET,
+                Collections.EMPTY_SET);
+    }
+
+    private org.apache.geronimo.gbean.GBeanData createGBeanData(ObjectName objectName) throws org.apache.geronimo.kernel.GBeanNotFoundException {
+        boolean running = isRunning(objectName);
+        if (running) {
+            ServiceInvoker serviceInvoker = getServiceInvoker(objectName);
+            return createGBeanData(serviceInvoker);
+        } else {
+            ServiceFactory serviceFactory = getServiceFactory(objectName);
+            return createGBeanData(objectName, serviceFactory);
+        }
+
+    }
+
+    private org.apache.geronimo.gbean.GBeanData createGBeanData(ServiceInvoker serviceInvoker) {
+        org.apache.geronimo.gbean.GBeanInfo gbeanInfo = createGBeanInfo(serviceInvoker);
+        org.apache.geronimo.gbean.GBeanData gbeanData = new org.apache.geronimo.gbean.GBeanData(serviceInvoker.getServiceName(), gbeanInfo);
+        for (Iterator iterator = gbeanInfo.getAttributes().iterator(); iterator.hasNext();) {
+            org.apache.geronimo.gbean.GAttributeInfo attribute = (org.apache.geronimo.gbean.GAttributeInfo) iterator.next();
+            if (attribute.isReadable()) {
+                try {
+                    String attributeName = attribute.getName();
+                    Object attributeValue = serviceInvoker.getAttribute(attributeName);
+                    gbeanData.setAttribute(attributeName, attributeValue);
+                } catch (Exception e) {
+                    // ignore
+                }
+            }
+        }
+        return gbeanData;
+    }
+
+    private org.apache.geronimo.gbean.GBeanData createGBeanData(ObjectName objectName, ServiceFactory serviceFactory) {
+        org.apache.geronimo.gbean.GBeanInfo gbeanInfo = createGBeanInfo(serviceFactory);
+        org.apache.geronimo.gbean.GBeanData gbeanData = new org.apache.geronimo.gbean.GBeanData(objectName, gbeanInfo);
+        if (serviceFactory instanceof ConfigurableServiceFactory) {
+            ConfigurableServiceFactory configurableServiceFactory = (ConfigurableServiceFactory) serviceFactory;
+            for (Iterator iterator = gbeanInfo.getAttributes().iterator(); iterator.hasNext();) {
+                org.apache.geronimo.gbean.GAttributeInfo attribute = (org.apache.geronimo.gbean.GAttributeInfo) iterator.next();
+                if (attribute.isReadable()) {
+                    try {
+                        String attributeName = attribute.getName();
+                        Object attributeValue = configurableServiceFactory.getProperty(attributeName);
+                        gbeanData.setAttribute(attributeName, attributeValue);
+                    } catch (Exception e) {
+                        // ignore
+                    }
+                }
+            }
+        }
+        return gbeanData;
+    }
+}
+
diff --git a/kernel/src/java/org/gbean/geronimo/KernelBridgeFactory.java b/kernel/src/java/org/gbean/geronimo/KernelBridgeFactory.java
new file mode 100644
index 0000000..8ee8758
--- /dev/null
+++ b/kernel/src/java/org/gbean/geronimo/KernelBridgeFactory.java
@@ -0,0 +1,27 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.geronimo;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class KernelBridgeFactory extends org.apache.geronimo.kernel.KernelFactory {
+    public org.apache.geronimo.kernel.Kernel createKernel(String kernelName) {
+//        return new KernelBridge(kernelName);
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/kernel/src/java/org/gbean/geronimo/LifecycleMonitorBridge.java b/kernel/src/java/org/gbean/geronimo/LifecycleMonitorBridge.java
new file mode 100644
index 0000000..8fed6b4
--- /dev/null
+++ b/kernel/src/java/org/gbean/geronimo/LifecycleMonitorBridge.java
@@ -0,0 +1,98 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.geronimo;
+
+import java.util.Set;
+import java.util.Map;
+import java.util.HashMap;
+import javax.management.ObjectName;
+
+import org.gbean.kernel.Kernel;
+import org.gbean.kernel.LifecycleListener;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class LifecycleMonitorBridge implements org.apache.geronimo.kernel.lifecycle.LifecycleMonitor {
+    private final Kernel kernel;
+    private final Map listenerBridgeMap = new HashMap();
+
+    public LifecycleMonitorBridge(Kernel kernel) {
+        this.kernel = kernel;
+    }
+
+    public void addLifecycleListener(org.apache.geronimo.kernel.lifecycle.LifecycleListener listener, ObjectName pattern) {
+        LifecycleListenerBridge listenerBridge = getListenerBridge(listener);
+        kernel.addLifecycleListener(listenerBridge, pattern);
+    }
+
+    public void addLifecycleListener(org.apache.geronimo.kernel.lifecycle.LifecycleListener listener, Set patterns) {
+        LifecycleListenerBridge listenerBridge = getListenerBridge(listener);
+        kernel.addLifecycleListener(listenerBridge, patterns);
+    }
+
+    public void removeLifecycleListener(org.apache.geronimo.kernel.lifecycle.LifecycleListener listener) {
+        LifecycleListenerBridge listenerBridge = getListenerBridge(listener);
+        if (listenerBridge != null) {
+            kernel.removeLifecycleListener(listenerBridge);
+        }
+    }
+
+    private LifecycleListenerBridge getListenerBridge(org.apache.geronimo.kernel.lifecycle.LifecycleListener listener) {
+        synchronized (listenerBridgeMap) {
+            LifecycleListenerBridge listenerBridge = (LifecycleListenerBridge) listenerBridgeMap.get(listener);
+            if (listenerBridge == null) {
+                listenerBridge = new LifecycleListenerBridge(listener);
+                listenerBridgeMap.put(listener, listenerBridge);
+            }
+            return listenerBridge;
+        }
+    }
+
+
+    private static class LifecycleListenerBridge implements LifecycleListener {
+        private final org.apache.geronimo.kernel.lifecycle.LifecycleListener lifecycleListener;
+
+        public LifecycleListenerBridge(org.apache.geronimo.kernel.lifecycle.LifecycleListener lifecycleListener) {
+            this.lifecycleListener = lifecycleListener;
+        }
+
+        public void loaded(ObjectName objectName) {
+            lifecycleListener.loaded(objectName);
+        }
+
+        public void starting(ObjectName objectName) {
+            lifecycleListener.starting(objectName);
+        }
+
+        public void running(ObjectName objectName) {
+            lifecycleListener.running(objectName);
+        }
+
+        public void stopping(ObjectName objectName) {
+            lifecycleListener.stopping(objectName);
+        }
+
+        public void stopped(ObjectName objectName) {
+            lifecycleListener.stopped(objectName);
+        }
+
+        public void unloaded(ObjectName objectName) {
+            lifecycleListener.unloaded(objectName);
+        }
+    }
+}
diff --git a/kernel/src/java/org/gbean/geronimo/LoaderBridge.java b/kernel/src/java/org/gbean/geronimo/LoaderBridge.java
new file mode 100644
index 0000000..38fad10
--- /dev/null
+++ b/kernel/src/java/org/gbean/geronimo/LoaderBridge.java
@@ -0,0 +1,43 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.geronimo;
+
+import java.net.URI;
+import javax.management.ObjectName;
+
+import org.apache.geronimo.kernel.config.ConfigurationStore;
+import org.gbean.loader.Loader;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class LoaderBridge implements Loader {
+    private final ConfigurationStore configurationStore;
+
+    public LoaderBridge(ConfigurationStore configurationStore) {
+        this.configurationStore = configurationStore;
+    }
+
+    public ObjectName load(String location) {
+        URI configurationUri = URI.create(location);
+        try {
+            return configurationStore.loadConfiguration(configurationUri);
+        } catch (Exception e) {
+            throw new RuntimeException("Error loading configuration: " + configurationUri, e);
+        }
+    }
+}
diff --git a/kernel/src/java/org/gbean/geronimo/ProxyManagerBridge.java b/kernel/src/java/org/gbean/geronimo/ProxyManagerBridge.java
new file mode 100644
index 0000000..26aa8af
--- /dev/null
+++ b/kernel/src/java/org/gbean/geronimo/ProxyManagerBridge.java
@@ -0,0 +1,73 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.geronimo;
+
+import javax.management.ObjectName;
+
+import org.gbean.proxy.ProxyManager;
+import org.gbean.proxy.ProxyFactory;
+import org.gbean.kernel.ServiceNotFoundException;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class ProxyManagerBridge implements org.apache.geronimo.kernel.proxy.ProxyManager {
+    private final ProxyManager proxyManager;
+
+    public ProxyManagerBridge(ProxyManager proxyManager) {
+        this.proxyManager = proxyManager;
+    }
+
+    public org.apache.geronimo.kernel.proxy.ProxyFactory createProxyFactory(Class type) {
+        return new ProxyFactoryBridge(proxyManager.createProxyFactory(type));
+    }
+
+    public Object createProxy(ObjectName target, Class type) {
+        try {
+            return proxyManager.createProxy(target, type);
+        } catch (ServiceNotFoundException e) {
+            throw (IllegalStateException) new IllegalStateException("Service was not loaded: " + target).initCause(e);
+        }
+    }
+
+    public synchronized void destroyProxy(Object proxy) {
+    }
+
+    public boolean isProxy(Object proxy) {
+        return proxyManager.isProxy(proxy);
+    }
+
+    public synchronized ObjectName getProxyTarget(Object proxy) {
+        return proxyManager.getProxyTarget(proxy);
+    }
+
+    private class ProxyFactoryBridge implements org.apache.geronimo.kernel.proxy.ProxyFactory {
+        private final ProxyFactory proxyFactory;
+
+        public ProxyFactoryBridge(ProxyFactory proxyFactory) {
+            this.proxyFactory = proxyFactory;
+        }
+
+        public Object createProxy(ObjectName target) {
+            try {
+                return proxyFactory.createProxy(target);
+            } catch (ServiceNotFoundException e) {
+                throw (IllegalStateException) new IllegalStateException("Service was not loaded: " + target).initCause(e);
+            }
+        }
+    }
+}
diff --git a/kernel/src/java/org/gbean/geronimo/ProxyReferenceCollection.java b/kernel/src/java/org/gbean/geronimo/ProxyReferenceCollection.java
new file mode 100644
index 0000000..711141f
--- /dev/null
+++ b/kernel/src/java/org/gbean/geronimo/ProxyReferenceCollection.java
@@ -0,0 +1,285 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.geronimo;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import javax.management.ObjectName;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.gbean.kernel.Kernel;
+import org.gbean.kernel.LifecycleAdapter;
+import org.gbean.kernel.LifecycleListener;
+import org.gbean.kernel.ServiceNotFoundException;
+import org.gbean.kernel.KernelUtil;
+import org.gbean.proxy.ProxyFactory;
+import org.gbean.proxy.ProxyManager;
+import org.springframework.beans.factory.FactoryBean;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class ProxyReferenceCollection implements org.apache.geronimo.gbean.ReferenceCollection, FactoryBeanProvider {
+    private static final Log log = LogFactory.getLog(ProxyReferenceCollection.class);
+    private final FactoryBean factoryBean;
+    private final String name;
+    private final ProxyFactory factory;
+    private final Map proxies = new HashMap();
+    private final Set listeners = new HashSet();
+    private boolean stopped = false;
+    private LifecycleListener listener;
+    private final Kernel kernel;
+
+    public ProxyReferenceCollection(FactoryBean factoryBean, String name, Class type, Kernel kernel, Set patterns) {
+        this.factoryBean = factoryBean;
+        this.name = name;
+        this.kernel = kernel;
+        try {
+            factory = ProxyManager.findProxyManager(kernel).createProxyFactory(type);
+        } catch (ServiceNotFoundException e) {
+            throw new IllegalStateException("No ProxyManager available in kernel");
+        }
+
+        Set targets = KernelUtil.getRunningServiceNames(kernel, patterns);
+        for (Iterator iterator = targets.iterator(); iterator.hasNext();) {
+            addTarget((ObjectName) iterator.next());
+        }
+
+        listener = new CollectionReferenceLifecycleListener();
+        kernel.addLifecycleListener(listener, patterns);
+    }
+
+    public FactoryBean getFactoryBean() {
+        return factoryBean;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public synchronized void destroy() {
+        stopped = true;
+        proxies.clear();
+        listeners.clear();
+
+        if (listener != null) {
+            kernel.removeLifecycleListener(listener);
+            listener = null;
+        }
+    }
+
+    private void addTarget(ObjectName target) {
+        Object proxy = null;
+        ArrayList listenerCopy;
+        synchronized (this) {
+            // if this is not a new target return
+            if (proxies.containsKey(target)) {
+                return;
+            }
+
+            // create and add the proxy
+            try {
+                proxy = factory.createProxy(target);
+            } catch (ServiceNotFoundException e) {
+                // service was removed before we could add it
+            }
+            proxies.put(target, proxy);
+
+            // make a snapshot of the listeners
+            listenerCopy = new ArrayList(listeners);
+        }
+
+        // fire the member added event
+        for (Iterator iterator = listenerCopy.iterator(); iterator.hasNext();) {
+            org.apache.geronimo.gbean.ReferenceCollectionListener listener = (org.apache.geronimo.gbean.ReferenceCollectionListener) iterator.next();
+            try {
+                listener.memberAdded(new org.apache.geronimo.gbean.ReferenceCollectionEvent(name, proxy));
+            } catch (Throwable t) {
+                log.error("Listener threw exception", t);
+            }
+        }
+    }
+
+    private void removeTarget(ObjectName target) {
+        Object proxy;
+        ArrayList listenerCopy;
+        synchronized (this) {
+            // remove the proxy
+            proxy = proxies.remove(target);
+
+            // if this was not a target return
+            if (proxy == null) {
+                return;
+            }
+
+            // make a snapshot of the listeners
+            listenerCopy = new ArrayList(listeners);
+        }
+
+        // fire the member removed event
+        for (Iterator iterator = listenerCopy.iterator(); iterator.hasNext();) {
+            org.apache.geronimo.gbean.ReferenceCollectionListener listener = (org.apache.geronimo.gbean.ReferenceCollectionListener) iterator.next();
+            try {
+                listener.memberRemoved(new org.apache.geronimo.gbean.ReferenceCollectionEvent(name, proxy));
+            } catch (Throwable t) {
+                log.error("Listener threw exception", t);
+            }
+        }
+    }
+
+    public synchronized boolean isStopped() {
+        return stopped;
+    }
+
+    public synchronized void addReferenceCollectionListener(org.apache.geronimo.gbean.ReferenceCollectionListener listener) {
+        listeners.add(listener);
+    }
+
+    public synchronized void removeReferenceCollectionListener(org.apache.geronimo.gbean.ReferenceCollectionListener listener) {
+        listeners.remove(listener);
+    }
+
+    public synchronized int size() {
+        if (stopped) {
+            return 0;
+        }
+        return proxies.size();
+    }
+
+    public synchronized boolean isEmpty() {
+        if (stopped) {
+            return true;
+        }
+        return proxies.isEmpty();
+    }
+
+    public synchronized boolean contains(Object o) {
+        if (stopped) {
+            return false;
+        }
+        return proxies.containsValue(o);
+    }
+
+    public synchronized Iterator iterator() {
+        if (stopped) {
+            return new Iterator() {
+                public boolean hasNext() {
+                    return false;
+                }
+
+                public Object next() {
+                    throw new NoSuchElementException();
+                }
+
+                public void remove() {
+                    throw new UnsupportedOperationException();
+                }
+            };
+        }
+
+        return new Iterator() {
+            // copy the proxies, so the client can iterate without concurrent modification
+            // this is necssary since the client has nothing to synchronize on
+            private final Iterator iterator = new ArrayList(proxies.values()).iterator();
+
+            public boolean hasNext() {
+                return iterator.hasNext();
+            }
+
+            public Object next() {
+                return iterator.next();
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    public synchronized Object[] toArray() {
+        if (stopped) {
+            return new Object[0];
+        }
+        return proxies.values().toArray();
+    }
+
+    public synchronized Object[] toArray(Object a[]) {
+        if (stopped) {
+            if (a.length > 0) {
+                a[0] = null;
+            }
+            return a;
+        }
+        return proxies.values().toArray(a);
+    }
+
+    public synchronized boolean containsAll(Collection c) {
+        if (stopped) {
+            return c.isEmpty();
+        }
+        return proxies.values().containsAll(c);
+    }
+
+    public boolean add(Object o) {
+        throw new UnsupportedOperationException();
+    }
+
+    public boolean remove(Object o) {
+        throw new UnsupportedOperationException();
+    }
+
+    public boolean addAll(Collection c) {
+        throw new UnsupportedOperationException();
+    }
+
+    public boolean removeAll(Collection c) {
+        throw new UnsupportedOperationException();
+    }
+
+    public boolean retainAll(Collection c) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void clear() {
+        throw new UnsupportedOperationException();
+    }
+
+    private class CollectionReferenceLifecycleListener extends LifecycleAdapter {
+        public void running(ObjectName objectName) {
+            addTarget(objectName);
+        }
+
+        public void stopping(ObjectName objectName) {
+            removeTarget(objectName);
+        }
+
+        public void stopped(ObjectName objectName) {
+            removeTarget(objectName);
+        }
+
+        public void unloaded(ObjectName objectName) {
+            removeTarget(objectName);
+        }
+    }
+}
diff --git a/kernel/src/java/org/gbean/geronimo/SingletonReference.java b/kernel/src/java/org/gbean/geronimo/SingletonReference.java
new file mode 100644
index 0000000..327ac9b
--- /dev/null
+++ b/kernel/src/java/org/gbean/geronimo/SingletonReference.java
@@ -0,0 +1,189 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.gbean.geronimo;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import javax.management.ObjectName;
+
+import org.gbean.kernel.ClassLoading;
+import org.gbean.kernel.KernelUtil;
+import org.gbean.kernel.ServiceNotFoundException;
+import org.gbean.proxy.ProxyManager;
+import org.gbean.service.ServiceContext;
+import org.gbean.spring.DependencyProvider;
+import org.gbean.spring.ServiceContextThreadLocal;
+import org.springframework.beans.MutablePropertyValues;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+
+/**
+ * @version $Rev: 71492 $ $Date: 2004-11-14 21:31:50 -0800 (Sun, 14 Nov 2004) $
+ */
+public class SingletonReference implements FactoryBean, DependencyProvider, Serializable {
+    public static BeanDefinitionHolder createBeanDefinition(String name, Set patterns, String referenceType) {
+        RootBeanDefinition beanDefinition = new RootBeanDefinition(SingletonReference.class, 0);
+        MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
+        propertyValues.addPropertyValue("name", name);
+        propertyValues.addPropertyValue("patterns", patterns);
+        propertyValues.addPropertyValue("referenceType", referenceType);
+        return new BeanDefinitionHolder(beanDefinition, SingletonReference.class.getName());
+    }
+
+    public static Map getDependencies(RootBeanDefinition beanDefinition) {
+        if (!beanDefinition.getBeanClass().equals(SingletonReference.class)) {
+            throw new IllegalArgumentException("Bean definition is for another bean type:" +
+                    " expected=" + SingletonReference.class.getName() +
+                    " actual=" + beanDefinition.getBeanClass().getName());
+        }
+        MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
+        if (!propertyValues.contains("name")) {
+            throw new IllegalArgumentException("Bean definition does not contain a name property");
+        }
+        String name = (String) propertyValues.getPropertyValue("name").getValue();
+        if (!propertyValues.contains("patterns")) {
+            throw new IllegalArgumentException("Bean definition does not contain a patterns property: name=" + name);
+        }
+        Set patterns = (Set) propertyValues.getPropertyValue("patterns").getValue();
+        return Collections.singletonMap(name, patterns);
+    }
+
+    /**
+     * Name of this reference.
+     */
+    private String name;
+
+    /**
+     * Proxy type which is injected into the service.
+     */
+    private String referenceType;
+
+    /**
+     * The target objectName patterns to watch for a connection.
+     */
+    private Set patterns;
+
+    /**
+     * Proxy type which is injected into the service.
+     */
+    private transient Class objectType;
+
+    public SingletonReference() {
+    }
+
+    public SingletonReference(String name, Set patterns, String referenceType) {
+        this.name = name;
+        this.patterns = patterns;
+        this.referenceType = referenceType;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getReferenceType() {
+        return referenceType;
+    }
+
+    public void setReferenceType(String referenceType) {
+        this.referenceType = referenceType;
+    }
+
+    public Set getPatterns() {
+        return patterns;
+    }
+
+    public void setPatterns(Set patterns) {
+        this.patterns = patterns;
+    }
+
+    public Map getDependencies() {
+        return Collections.singletonMap(name, patterns);
+    }
+
+    public final Class getObjectType() {
+        ServiceContext serviceContext = ServiceContextThreadLocal.get();
+        if (serviceContext == null) {
+            throw new IllegalStateException("Service context has not been set");
+        }
+        synchronized (this) {
+            if (objectType == null) {
+                try {
+                    objectType = ClassLoading.loadClass(referenceType, serviceContext.getClassLoader());
+                } catch (ClassNotFoundException e) {
+                    throw new IllegalStateException("Could not load singleton reference object type");
+                }
+            }
+        }
+        return objectType;
+    }
+
+    public synchronized final Object getObject() throws ClassNotFoundException {
+        ServiceContext serviceContext = ServiceContextThreadLocal.get();
+        if (serviceContext == null) {
+            throw new IllegalStateException("Service context has not been set");
+        }
+        synchronized (this) {
+            if (objectType == null) {
+                objectType = ClassLoading.loadClass(referenceType, serviceContext.getClassLoader());
+            }
+        }
+
+        Set targets = KernelUtil.getRunningServiceNames(serviceContext.getKernel(), patterns);
+        if (targets.size() != 1) {
+            throw new IllegalStateException("Invalid reference: name=" + name + ", targetCount=" + targets.size() + ", patterns=" + getPatternsText());
+        }
+
+        // add a dependency on our target and create the proxy
+        ObjectName target = (ObjectName) targets.iterator().next();
+        ProxyManager proxyManager = null;
+        try {
+            proxyManager = ProxyManager.findProxyManager(serviceContext.getKernel());
+        } catch (ServiceNotFoundException e) {
+            throw new IllegalStateException("ProxyManger has not been loaded");
+
+        }
+
+        try {
+            return proxyManager.createProxy(target, objectType, this);
+        } catch (ServiceNotFoundException e) {
+            throw new IllegalStateException("Referenced object was unregistered before a proxy could be created: name=" + name + ", targetCount=" + targets.size() + ", patterns=" + getPatternsText());
+        }
+    }
+
+    public boolean isSingleton() {
+        return true;
+    }
+
+    private String getPatternsText() {
+        StringBuffer buf = new StringBuffer();
+        for (Iterator iterator = patterns.iterator(); iterator.hasNext();) {
+            ObjectName objectName = (ObjectName) iterator.next();
+            buf.append(objectName.getCanonicalName()).append(" ");
+        }
+        return buf.toString();
+    }
+}
diff --git a/kernel/src/java/org/gbean/jmx/JMXBridge.java b/kernel/src/java/org/gbean/jmx/JMXBridge.java
new file mode 100644
index 0000000..3e6e71d
--- /dev/null
+++ b/kernel/src/java/org/gbean/jmx/JMXBridge.java
@@ -0,0 +1,180 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.jmx;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.JMException;
+import javax.management.JMRuntimeException;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.ObjectName;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.gbean.kernel.Kernel;
+import org.gbean.kernel.LifecycleAdapter;
+import org.gbean.kernel.ServiceName;
+import org.gbean.kernel.ServiceNotFoundException;
+import org.gbean.kernel.simple.SimpleLifecycle;
+import org.gbean.reflect.ServiceInvokerManager;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class JMXBridge implements SimpleLifecycle {
+    private static final ObjectName ALL = ServiceName.createName("*:*");
+    private static final Log log = LogFactory.getLog(JMXBridge.class);
+
+    private final HashMap registry = new HashMap();
+    private final Kernel kernel;
+    private final ServiceInvokerManager serviceInvokerManager;
+    private final MBeanServer mbeanServer;
+
+    public JMXBridge(Kernel kernel, ServiceInvokerManager serviceInvokerManager) {
+        this.kernel = kernel;
+        this.serviceInvokerManager = serviceInvokerManager;
+        mbeanServer = MBeanServerFactory.createMBeanServer(kernel.getKernelName());
+    }
+
+    public JMXBridge(Kernel kernel, ServiceInvokerManager serviceInvokerManager, String mbeanServerId) {
+        this.kernel = kernel;
+        this.serviceInvokerManager = serviceInvokerManager;
+        ArrayList servers = MBeanServerFactory.findMBeanServer(mbeanServerId);
+        if (servers.size() == 0) {
+            throw new IllegalStateException("No MBeanServers were found with the agent id " + mbeanServerId);
+        } else if (servers.size() > 1) {
+            throw new IllegalStateException(servers.size() + " MBeanServers were found with the agent id " + mbeanServerId);
+        }
+        mbeanServer = (MBeanServer) servers.get(0);
+    }
+
+    public JMXBridge(Kernel kernel, ServiceInvokerManager serviceInvokerManager, MBeanServer mbeanServer) {
+        this.kernel = kernel;
+        this.serviceInvokerManager = serviceInvokerManager;
+        this.mbeanServer = mbeanServer;
+    }
+
+    public void start() {
+        kernel.addLifecycleListener(new ServiceRegistrationListener(), ALL);
+
+        HashMap beans = new HashMap();
+        synchronized (this) {
+            Set allNames = kernel.listServiceNames(ALL);
+            for (Iterator iterator = allNames.iterator(); iterator.hasNext();) {
+                ObjectName objectName = (ObjectName) iterator.next();
+                if (registry.containsKey(objectName)) {
+                    // instance already registered
+                    continue;
+                }
+                try {
+                    ServiceMBean serviceMBean = new ServiceMBean(kernel, serviceInvokerManager, objectName);
+                    registry.put(objectName, serviceMBean);
+                    beans.put(objectName, serviceMBean);
+                } catch (ServiceNotFoundException e) {
+                    // ignore - service died on us
+                }
+            }
+        }
+        for (Iterator iterator = beans.entrySet().iterator(); iterator.hasNext();) {
+            Map.Entry entry = (Map.Entry) iterator.next();
+            ObjectName objectName = (ObjectName) entry.getKey();
+            ServiceMBean serviceMBean = (ServiceMBean) entry.getValue();
+            try {
+                mbeanServer.registerMBean(serviceMBean, objectName);
+            } catch (InstanceAlreadyExistsException e) {
+                // ignore - service already has an mbean shadow object
+            } catch (Exception e) {
+                log.info("Unable to register MBean shadow object for service: " + objectName, unwrapJMException(e));
+            }
+        }
+    }
+
+    public void stop() {
+        // unregister all of our mbeans from the MBeanServer
+        Map beans;
+        synchronized (this) {
+            beans = new HashMap(registry);
+            registry.clear();
+        }
+        for (Iterator i = beans.keySet().iterator(); i.hasNext();) {
+            ObjectName objectName = (ObjectName) i.next();
+            try {
+                mbeanServer.unregisterMBean(objectName);
+            } catch (Exception e) {
+                // ignore
+            }
+        }
+    }
+
+    private void register(ObjectName objectName) {
+        try {
+            ServiceMBean serviceMBean = null;
+            synchronized (this) {
+                if (registry.containsKey(objectName)) {
+                    return;
+                }
+                serviceMBean = new ServiceMBean(kernel, serviceInvokerManager, objectName);
+                registry.put(objectName, serviceMBean);
+            }
+            mbeanServer.registerMBean(serviceMBean, objectName);
+        } catch (InstanceAlreadyExistsException e) {
+            // ignore - service already has a mbean shadow object
+        } catch (Exception e) {
+            log.info("Unable to register MBean shadow object for service", unwrapJMException(e));
+        }
+    }
+
+    private void unregister(ObjectName objectName) {
+        synchronized (this) {
+            if (registry.remove(objectName) == null) {
+                return;
+            }
+        }
+
+        try {
+            mbeanServer.unregisterMBean(objectName);
+        } catch (InstanceNotFoundException e) {
+            // ignore - something else may have unregistered us
+            // if there truely is no service then we will catch it below whwn we call the superclass
+        } catch (Exception e) {
+            log.info("Unable to unregister MBean shadow object for service", unwrapJMException(e));
+        }
+    }
+
+    private Throwable unwrapJMException(Throwable cause) {
+        while ((cause instanceof JMException || cause instanceof JMRuntimeException) && cause.getCause() != null) {
+            cause = cause.getCause();
+        }
+        return cause;
+    }
+
+    private class ServiceRegistrationListener extends LifecycleAdapter {
+        public void loaded(ObjectName objectName) {
+            register(objectName);
+        }
+
+        public void unloaded(ObjectName objectName) {
+            unregister(objectName);
+        }
+    }
+}
diff --git a/kernel/src/java/org/gbean/jmx/NotificationType.java b/kernel/src/java/org/gbean/jmx/NotificationType.java
new file mode 100644
index 0000000..7d71a74
--- /dev/null
+++ b/kernel/src/java/org/gbean/jmx/NotificationType.java
@@ -0,0 +1,79 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.gbean.jmx;
+
+
+/**
+ * Static constants class which contains all of the J2EE notification types from the
+ * J2EE management specification.
+ *
+ * @version $Rev: 109314 $ $Date: 2004-12-01 00:01:37 -0800 (Wed, 01 Dec 2004) $
+ */
+public final class NotificationType {
+    private NotificationType() {
+    }
+
+    /**
+     * A new managed object was created.
+     */
+    public static final String OBJECT_CREATED = "j2ee.object.created";
+
+    /**
+     * A managed object was deleted
+     */
+    public static final String OBJECT_DELETED = "j2ee.object.deleted";
+
+    /**
+     * A state manageable object entered the starting state
+     */
+    public static final String STATE_STARTING = "j2ee.state.starting";
+
+    /**
+     * A state manageable object entered the running state
+     */
+    public static final String STATE_RUNNING = "j2ee.state.running";
+
+    /**
+     * A state manageable object entered the stopping state
+     */
+    public static final String STATE_STOPPING = "j2ee.state.stopping";
+
+    /**
+     * A state manageable object entered the stopped state.
+     */
+    public static final String STATE_STOPPED = "j2ee.state.stopped";
+
+    /**
+     * A state manageable object entered the failed state
+     */
+    public static final String STATE_FAILED = "j2ee.state.failed";
+
+    /**
+     * An attribute has change value
+     */
+    public static final String ATTRIBUTE_CHANGED = "j2ee.attribute.changed";
+
+    /**
+     * An array containg all of the know J2EE notification types
+     */
+    public static final String[] TYPES = new String[]{
+        OBJECT_CREATED, OBJECT_DELETED,
+        STATE_STARTING, STATE_RUNNING, STATE_STOPPING, STATE_STOPPED, STATE_FAILED,
+        ATTRIBUTE_CHANGED
+    };
+}
diff --git a/kernel/src/java/org/gbean/jmx/ServiceMBean.java b/kernel/src/java/org/gbean/jmx/ServiceMBean.java
new file mode 100644
index 0000000..e5c2e70
--- /dev/null
+++ b/kernel/src/java/org/gbean/jmx/ServiceMBean.java
@@ -0,0 +1,456 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.gbean.jmx;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+import javax.management.DynamicMBean;
+import javax.management.JMException;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanConstructorInfo;
+import javax.management.MBeanInfo;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanParameterInfo;
+import javax.management.MBeanServer;
+import javax.management.Notification;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.NotificationEmitter;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.gbean.kernel.Kernel;
+import org.gbean.kernel.LifecycleListener;
+import org.gbean.kernel.NoSuchAttributeException;
+import org.gbean.kernel.NoSuchOperationException;
+import org.gbean.kernel.OperationSignature;
+import org.gbean.kernel.ServiceNotFoundException;
+import org.gbean.kernel.runtime.ServiceState;
+import org.gbean.reflect.OperationInvoker;
+import org.gbean.reflect.PropertyInvoker;
+import org.gbean.reflect.ServiceInvoker;
+import org.gbean.reflect.ServiceInvokerManager;
+import org.gbean.service.ConfigurableServiceFactory;
+import org.gbean.service.ServiceFactory;
+
+/**
+ * @version $Rev: 109772 $ $Date: 2004-12-03 21:06:02 -0800 (Fri, 03 Dec 2004) $
+ */
+public final class ServiceMBean implements DynamicMBean, NotificationEmitter {
+    private static final Log log = LogFactory.getLog(ServiceMBean.class);
+
+    private static final String ATTRIBUTE_CLASS_LOADER = "classLoader";
+    private static final String ATTRIBUTE_STATE = "state";
+    private static final String ATTRIBUTE_START_TIME = "startTime";
+    private static final String ATTRIBUTE_STATE_MANAGEABLE = "stateManageable";
+    private static final String ATTRIBUTE_STATISTICS_PROVIDER = "statisticsProvider";
+    private static final String ATTRIBUTE_EVENT_PROVIDER = "eventProvider";
+    private static final String ATTRIBUTE_EVENT_TYPES = "eventTypes";
+    private static final String ATTRIBUTE_GBEAN_ENABLED = "gbeanEnabled";
+
+    private static final String OPERATION_START = "start";
+    private static final String OPERATION_START_RECURSIVE = "startRecursive";
+    private static final String OPERATION_STOP = "stop";
+
+    /**
+     * The kernel
+     */
+    private final Kernel kernel;
+
+    /**
+     * The unique name of this service.
+     */
+    private final ObjectName objectName;
+
+    /**
+     * The broadcaster for notifications
+     */
+    private final NotificationBroadcasterSupport notificationBroadcaster = new NotificationBroadcasterSupport();
+
+    /**
+     * Listenes for kernel lifecycle events for this service and broadcasts them via JMX.
+     */
+    private final LifecycleBridge lifecycleBridge;
+
+    /**
+     * The service invocation manager from which we get the service invoker
+     */
+    private final ServiceInvokerManager serviceInvokerManager;
+
+    /**
+     * The factory for this service
+     */
+    private final ServiceFactory serviceFactory;
+
+    /**
+     * The service invoker
+     */
+    private ServiceInvoker serviceInvoker;
+
+    public ServiceMBean(Kernel kernel, ServiceInvokerManager serviceInvokerManager, ObjectName objectName) throws ServiceNotFoundException {
+        this.kernel = kernel;
+        serviceFactory = kernel.getServiceFactory(objectName);
+        this.serviceInvokerManager = serviceInvokerManager;
+        this.objectName = objectName;
+        lifecycleBridge = new LifecycleBridge(notificationBroadcaster);
+    }
+
+    public ObjectName getObjectName() {
+        return objectName;
+    }
+
+    public ObjectName preRegister(MBeanServer mBeanServer, ObjectName objectName) throws Exception {
+        return objectName;
+    }
+
+    public synchronized void postRegister(Boolean registrationDone) {
+        if (Boolean.TRUE.equals(registrationDone)) {
+            // fire the loaded event from the mbean.. it was already fired from the GBeanInstance when it was created
+            kernel.addLifecycleListener(lifecycleBridge, objectName);
+            lifecycleBridge.loaded(objectName);
+            updateState();
+        }
+    }
+
+    public synchronized void preDeregister() {
+        kernel.removeLifecycleListener(lifecycleBridge);
+        lifecycleBridge.unloaded(objectName);
+        serviceInvoker = null;
+    }
+
+    public void postDeregister() {
+    }
+
+    private synchronized void updateState() {
+        try {
+            int serviceState = kernel.getServiceState(objectName);
+            boolean running = serviceState == ServiceState.RUNNING_INDEX || serviceState == ServiceState.STOPPING_INDEX;
+            if (running) {
+                serviceInvoker = serviceInvokerManager.getServiceInvoker(objectName);
+                return;
+            }
+        } catch (Exception e) {
+            // ignore cleaned up below
+        }
+        serviceInvoker = null;
+    }
+
+    public synchronized MBeanInfo getMBeanInfo() {
+        try {
+            String className;
+            String description = "No description available";
+            MBeanAttributeInfo[] attributes;
+            MBeanOperationInfo[] operations;
+
+            if (serviceInvoker != null) {
+                className = serviceInvoker.getServiceType().getName();
+
+                // attributes
+                List propertyIndex = serviceInvoker.getPropertyIndex();
+                attributes = new MBeanAttributeInfo[propertyIndex.size()];
+                for (ListIterator iterator = propertyIndex.listIterator(); iterator.hasNext();) {
+                    PropertyInvoker propertyInvoker = (PropertyInvoker) iterator.next();
+
+                    boolean isIs = false;
+                    if (propertyInvoker.isReadable()) {
+                        isIs = propertyInvoker.getGetterSignature().getName().startsWith("is");
+                    }
+
+                    attributes[iterator.previousIndex()] = new MBeanAttributeInfo(propertyInvoker.getPropertyName(),
+                            propertyInvoker.getType().getName(),
+                            "no description available",
+                            propertyInvoker.isReadable(),
+                            propertyInvoker.isWritable(),
+                            isIs);
+                }
+
+
+                // operations
+                List operationIndex = serviceInvoker.getOperationIndex();
+                operations = new MBeanOperationInfo[operationIndex.size()];
+                for (ListIterator iterator = operationIndex.listIterator(); iterator.hasNext();) {
+                    OperationInvoker operationInvoker = (OperationInvoker) iterator.next();
+
+                    OperationSignature signature = operationInvoker.getSignature();
+
+                    List argumentTypes = signature.getParameterTypes();
+                    MBeanParameterInfo[] parameters = new MBeanParameterInfo[argumentTypes.size()];
+                    for (ListIterator argIterator = argumentTypes.listIterator(); argIterator.hasNext();) {
+                        String type = (String) argIterator.next();
+                        parameters[argIterator.previousIndex()] = new MBeanParameterInfo("parameter" + argIterator.previousIndex(),
+                                type,
+                                "no description available");
+                    }
+
+                    operations[iterator.previousIndex()] = new MBeanOperationInfo(signature.getName(), "no description available", parameters, "java.lang.Object", MBeanOperationInfo.UNKNOWN);
+                }
+            } else {
+                className = Object.class.getName();
+                operations = new MBeanOperationInfo[0];
+                if (serviceFactory instanceof ConfigurableServiceFactory) {
+                    ConfigurableServiceFactory configurableServiceFactory = (ConfigurableServiceFactory) serviceFactory;
+                    List propertyNames = new ArrayList(configurableServiceFactory.getPropertyNames());
+                    attributes = new MBeanAttributeInfo[propertyNames.size()];
+                    for (ListIterator iterator = propertyNames.listIterator(); iterator.hasNext();) {
+                        String propertyName = (String) iterator.next();
+                        attributes[iterator.previousIndex()] = new MBeanAttributeInfo(propertyName,
+                                Object.class.getName(),
+                                "no description available",
+                                true,
+                                true,
+                                false);
+                    }
+                } else {
+                    attributes = new MBeanAttributeInfo[0];
+                }
+
+            }
+
+            MBeanNotificationInfo[] notifications = new MBeanNotificationInfo[1];
+            notifications[0] = new MBeanNotificationInfo(NotificationType.TYPES, "javax.management.Notification", "J2EE Notifications");
+
+            MBeanInfo mbeanInfo = new MBeanInfo(className, description, attributes, new MBeanConstructorInfo[0], operations, notifications);
+            return mbeanInfo;
+        } catch (RuntimeException e) {
+            log.info("Unable to create MBeanInfo", e);
+            throw e;
+        }
+    }
+
+    public Object getAttribute(String attributeName) throws ReflectionException, AttributeNotFoundException {
+        try {
+            if (ATTRIBUTE_CLASS_LOADER.equals(attributeName)) {
+                return kernel.getClassLoaderFor(objectName);
+            } else if (ATTRIBUTE_STATE.equals(attributeName)) {
+                return new Integer(kernel.getServiceState(objectName));
+            } else if (ATTRIBUTE_START_TIME.equals(attributeName)) {
+                return new Long(kernel.getServiceStartTime(objectName));
+            } else if (ATTRIBUTE_STATE_MANAGEABLE.equals(attributeName)) {
+                return Boolean.TRUE;
+            } else if (ATTRIBUTE_STATISTICS_PROVIDER.equals(attributeName)) {
+                return Boolean.FALSE;
+            } else if (ATTRIBUTE_EVENT_PROVIDER.equals(attributeName)) {
+                return Boolean.TRUE;
+            } else if (ATTRIBUTE_EVENT_TYPES.equals(attributeName)) {
+                return NotificationType.TYPES;
+            } else if (ATTRIBUTE_GBEAN_ENABLED.equals(attributeName)) {
+                return Boolean.valueOf(kernel.isServiceEnabled(objectName));
+            }
+
+            ServiceInvoker serviceInvoker;
+            synchronized (this) {
+                serviceInvoker = this.serviceInvoker;
+            }
+
+            if (serviceInvoker != null) {
+                Object value = serviceInvoker.getAttribute(attributeName);
+                return value;
+            } else {
+                if (!(serviceFactory instanceof ConfigurableServiceFactory)) {
+                    throw new AttributeNotFoundException("Service is stopped and the service factory not configurable: objectName=" + objectName + ", propertyName=" + attributeName);
+                }
+                ConfigurableServiceFactory configurableServiceFactory = (ConfigurableServiceFactory) serviceFactory;
+                return configurableServiceFactory.getProperty(attributeName);
+            }
+        } catch (AttributeNotFoundException e) {
+            throw e;
+        } catch (NoSuchAttributeException e) {
+            throw new AttributeNotFoundException(attributeName);
+        } catch (Exception e) {
+            throw new ReflectionException(e);
+        }
+    }
+
+    public void setAttribute(Attribute attribute) throws ReflectionException, AttributeNotFoundException {
+        String attributeName = attribute.getName();
+        Object attributeValue = attribute.getValue();
+        try {
+            if (ATTRIBUTE_GBEAN_ENABLED.equals(attributeName)) {
+                kernel.setServiceEnabled(objectName, ((Boolean) attributeValue).booleanValue());
+            } else {
+                ServiceInvoker serviceInvoker;
+                synchronized (this) {
+                    serviceInvoker = this.serviceInvoker;
+                }
+
+                if (serviceInvoker != null) {
+                    serviceInvoker.setAttribute(attributeName, attributeValue);
+                } else {
+                    if (!(serviceFactory instanceof ConfigurableServiceFactory)) {
+                        throw new AttributeNotFoundException("Service is stopped and the service factory not configurable: objectName=" + objectName + ", propertyName=" + attributeName);
+                    }
+                    ConfigurableServiceFactory configurableServiceFactory = (ConfigurableServiceFactory) serviceFactory;
+                    configurableServiceFactory.setProperty(attributeName, attributeValue);
+                }
+            }
+        } catch (AttributeNotFoundException e) {
+            throw e;
+        } catch (NoSuchAttributeException e) {
+            throw new AttributeNotFoundException(attributeName);
+        } catch (Exception e) {
+            throw new ReflectionException(e);
+        }
+    }
+
+    public AttributeList getAttributes(String[] attributes) {
+        AttributeList results = new AttributeList(attributes.length);
+        for (int i = 0; i < attributes.length; i++) {
+            String name = attributes[i];
+            try {
+                Object value = getAttribute(name);
+                results.add(new Attribute(name, value));
+            } catch (JMException e) {
+                log.warn("Exception while getting attribute " + name, e);
+            }
+        }
+        return results;
+    }
+
+    public AttributeList setAttributes(AttributeList attributes) {
+        AttributeList results = new AttributeList(attributes.size());
+        for (Iterator iterator = attributes.iterator(); iterator.hasNext();) {
+            Attribute attribute = (Attribute) iterator.next();
+            try {
+                setAttribute(attribute);
+                results.add(attribute);
+            } catch (JMException e) {
+                log.warn("Exception while setting attribute " + attribute.getName(), e);
+            }
+        }
+        return results;
+    }
+
+    public Object invoke(String operationName, Object[] arguments, String[] types) throws ReflectionException {
+        try {
+            if (arguments.length == 0 && OPERATION_START.equals(operationName)) {
+                kernel.startService(objectName);
+                return null;
+            } else if (arguments.length == 0 && OPERATION_START_RECURSIVE.equals(operationName)) {
+                kernel.startRecursiveService(objectName);
+                return null;
+            } else if (arguments.length == 0 && OPERATION_STOP.equals(operationName)) {
+                kernel.startService(objectName);
+                return null;
+            } else {
+                ServiceInvoker serviceInvoker;
+                synchronized (this) {
+                    serviceInvoker = this.serviceInvoker;
+                }
+                if (serviceInvoker == null) {
+                    throw new IllegalStateException("Service is not running: name=" + objectName);
+                }
+                return serviceInvoker.invoke(operationName, arguments, types);
+            }
+        } catch (NoSuchOperationException e) {
+            throw new ReflectionException(new NoSuchMethodException(new OperationSignature(operationName, types).toString()));
+        } catch (Exception e) {
+            throw new ReflectionException(e);
+        }
+    }
+
+    public MBeanNotificationInfo[] getNotificationInfo() {
+        return new MBeanNotificationInfo[]{
+            new MBeanNotificationInfo(NotificationType.TYPES, "javax.management.Notification", "J2EE Notifications")
+        };
+    }
+
+    public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) {
+        notificationBroadcaster.addNotificationListener(listener, filter, handback);
+    }
+
+    public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException {
+        notificationBroadcaster.removeNotificationListener(listener);
+    }
+
+    public void removeNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws ListenerNotFoundException {
+        notificationBroadcaster.removeNotificationListener(listener, filter, handback);
+    }
+
+    public String toString() {
+        return objectName.toString();
+    }
+
+    private class LifecycleBridge implements LifecycleListener {
+        /**
+         * Sequence number used for notifications
+         */
+        private long sequence;
+
+        /**
+         * The notification broadcaster to use
+         */
+        private final NotificationBroadcasterSupport notificationBroadcaster;
+
+        public LifecycleBridge(NotificationBroadcasterSupport notificationBroadcaster) {
+            this.notificationBroadcaster = notificationBroadcaster;
+        }
+
+        public void loaded(ObjectName objectName) {
+            if (objectName.equals(objectName)) {
+                notificationBroadcaster.sendNotification(new Notification(NotificationType.OBJECT_CREATED, objectName, nextSequence()));
+            }
+        }
+
+        public void starting(ObjectName objectName) {
+            if (objectName.equals(objectName)) {
+                notificationBroadcaster.sendNotification(new Notification(NotificationType.STATE_STARTING, objectName, nextSequence()));
+            }
+        }
+
+        public void running(ObjectName objectName) {
+            if (objectName.equals(objectName)) {
+                updateState();
+                notificationBroadcaster.sendNotification(new Notification(NotificationType.STATE_RUNNING, objectName, nextSequence()));
+            }
+        }
+
+        public void stopping(ObjectName objectName) {
+            if (objectName.equals(objectName)) {
+                notificationBroadcaster.sendNotification(new Notification(NotificationType.STATE_STOPPING, objectName, nextSequence()));
+            }
+        }
+
+        public void stopped(ObjectName objectName) {
+            if (objectName.equals(objectName)) {
+                updateState();
+                notificationBroadcaster.sendNotification(new Notification(NotificationType.STATE_STOPPED, objectName, nextSequence()));
+            }
+        }
+
+        public void unloaded(ObjectName objectName) {
+            if (objectName.equals(objectName)) {
+                updateState();
+                notificationBroadcaster.sendNotification(new Notification(NotificationType.OBJECT_DELETED, objectName, nextSequence()));
+            }
+        }
+
+        public synchronized long nextSequence() {
+            return sequence++;
+        }
+    }
+}
diff --git a/kernel/src/java/org/gbean/kernel/ClassLoading.java b/kernel/src/java/org/gbean/kernel/ClassLoading.java
new file mode 100644
index 0000000..44f4b58
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/ClassLoading.java
@@ -0,0 +1,225 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.gbean.kernel;
+
+import java.lang.reflect.Array;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Utilities for loading classes.
+ *
+ * @version $Rev: 169154 $ $Date: 2005-05-08 12:35:23 -0700 (Sun, 08 May 2005) $
+ */
+public class ClassLoading {
+    /**
+     * Load a class for the given name.
+     * <p/>
+     * <p>Handles loading primitive types as well as VM class and array syntax.
+     *
+     * @param className The name of the Class to be loaded.
+     * @param classLoader The class loader to load the Class object from.
+     * @return The Class object for the given name.
+     * @throws ClassNotFoundException Failed to load Class object.
+     */
+    public static Class loadClass(final String className, final ClassLoader classLoader) throws ClassNotFoundException {
+        if (className == null) {
+            throw new IllegalArgumentException("className is null");
+        }
+        if (classLoader == null) {
+            throw new IllegalArgumentException("classLoader is null");
+        }
+
+        // First just try to load
+        try {
+            return classLoader.loadClass(className);
+        } catch (ClassNotFoundException ignore) {
+            // handle special cases below
+        }
+
+        Class type = null;
+
+        // Check if it is a primitive type
+        type = getPrimitiveType(className);
+        if (type != null) return type;
+
+        // Check if it is a vm primitive
+        type = getVMPrimitiveType(className);
+        if (type != null) return type;
+
+        // Handle VM class syntax (Lclassname;)
+        if (className.charAt(0) == 'L' && className.charAt(className.length() - 1) == ';') {
+            return classLoader.loadClass(className.substring(1, className.length() - 1));
+        }
+
+        // Handle VM array syntax ([type)
+        if (className.charAt(0) == '[') {
+            int arrayDimension = className.lastIndexOf('[') + 1;
+            String componentClassName = className.substring(arrayDimension, className.length());
+            type = loadClass(componentClassName, classLoader);
+
+            int dim[] = new int[arrayDimension];
+            java.util.Arrays.fill(dim, 0);
+            return Array.newInstance(type, dim).getClass();
+        }
+
+        // Handle user friendly type[] syntax
+        if (className.endsWith("[]")) {
+            // get the base component class name and the arrayDimensions
+            int arrayDimension = 0;
+            String componentClassName = className;
+            while (componentClassName.endsWith("[]")) {
+                componentClassName = componentClassName.substring(0, componentClassName.length() - 2);
+                arrayDimension++;
+            }
+
+            // load the base type
+            type = loadClass(componentClassName, classLoader);
+
+            // return the array type
+            int[] dim = new int[arrayDimension];
+            java.util.Arrays.fill(dim, 0);
+            return Array.newInstance(type, dim).getClass();
+        }
+
+        // Else we can not load (give up)
+        throw new ClassNotFoundException(className);
+    }
+
+    public static String getClassName(Class clazz) {
+        StringBuffer rc = new StringBuffer();
+        while (clazz.isArray()) {
+            rc.append('[');
+            clazz = clazz.getComponentType();
+        }
+        if (!clazz.isPrimitive()) {
+            rc.append('L');
+            rc.append(clazz.getName());
+            rc.append(';');
+        } else {
+            rc.append(VM_PRIMITIVES_REVERSE.get(clazz));
+        }
+        return rc.toString();
+    }
+
+    /**
+     * Primitive type name -> class map.
+     */
+    private static final Map PRIMITIVES = new HashMap();
+
+    /** Setup the primitives map. */
+    static {
+        PRIMITIVES.put("boolean", Boolean.TYPE);
+        PRIMITIVES.put("byte", Byte.TYPE);
+        PRIMITIVES.put("char", Character.TYPE);
+        PRIMITIVES.put("short", Short.TYPE);
+        PRIMITIVES.put("int", Integer.TYPE);
+        PRIMITIVES.put("long", Long.TYPE);
+        PRIMITIVES.put("float", Float.TYPE);
+        PRIMITIVES.put("double", Double.TYPE);
+        PRIMITIVES.put("void", Void.TYPE);
+    }
+
+    /**
+     * Get the primitive type for the given primitive name.
+     *
+     * @param name Primitive type name (boolean, byte, int, ...)
+     * @return Primitive type or null.
+     */
+    private static Class getPrimitiveType(final String name) {
+        return (Class) PRIMITIVES.get(name);
+    }
+
+    /**
+     * VM primitive type name -> primitive type
+     */
+    private static final HashMap VM_PRIMITIVES = new HashMap();
+
+    /** Setup the vm primitives map. */
+    static {
+        VM_PRIMITIVES.put("B", byte.class);
+        VM_PRIMITIVES.put("C", char.class);
+        VM_PRIMITIVES.put("D", double.class);
+        VM_PRIMITIVES.put("F", float.class);
+        VM_PRIMITIVES.put("I", int.class);
+        VM_PRIMITIVES.put("J", long.class);
+        VM_PRIMITIVES.put("S", short.class);
+        VM_PRIMITIVES.put("Z", boolean.class);
+        VM_PRIMITIVES.put("V", void.class);
+    }
+
+    /**
+     * VM primitive type primitive type ->  name
+     */
+    private static final HashMap VM_PRIMITIVES_REVERSE = new HashMap();
+
+    /** Setup the vm primitives reverse map. */
+    static {
+        VM_PRIMITIVES_REVERSE.put(byte.class, "B");
+        VM_PRIMITIVES_REVERSE.put(char.class, "C");
+        VM_PRIMITIVES_REVERSE.put(double.class, "D");
+        VM_PRIMITIVES_REVERSE.put(float.class, "F");
+        VM_PRIMITIVES_REVERSE.put(int.class, "I");
+        VM_PRIMITIVES_REVERSE.put(long.class, "J");
+        VM_PRIMITIVES_REVERSE.put(short.class, "S");
+        VM_PRIMITIVES_REVERSE.put(boolean.class, "Z");
+        VM_PRIMITIVES_REVERSE.put(void.class, "V");
+    }
+
+    /**
+     * Get the primitive type for the given VM primitive name.
+     * <p/>
+     * <p>Mapping:
+     * <pre>
+     *   B - byte
+     *   C - char
+     *   D - double
+     *   F - float
+     *   I - int
+     *   J - long
+     *   S - short
+     *   Z - boolean
+     *   V - void
+     * </pre>
+     *
+     * @param name VM primitive type name (B, C, J, ...)
+     * @return Primitive type or null.
+     */
+    private static Class getVMPrimitiveType(final String name) {
+        return (Class) VM_PRIMITIVES.get(name);
+    }
+
+    /**
+     * Map of primitive types to their wrapper classes
+     */
+    private static final Map PRIMITIVE_WRAPPERS = new HashMap();
+
+    /** Setup the wrapper map. */
+    static {
+        PRIMITIVE_WRAPPERS.put(Boolean.TYPE, Boolean.class);
+        PRIMITIVE_WRAPPERS.put(Byte.TYPE, Byte.class);
+        PRIMITIVE_WRAPPERS.put(Character.TYPE, Character.class);
+        PRIMITIVE_WRAPPERS.put(Double.TYPE, Double.class);
+        PRIMITIVE_WRAPPERS.put(Float.TYPE, Float.class);
+        PRIMITIVE_WRAPPERS.put(Integer.TYPE, Integer.class);
+        PRIMITIVE_WRAPPERS.put(Long.TYPE, Long.class);
+        PRIMITIVE_WRAPPERS.put(Short.TYPE, Short.class);
+        PRIMITIVE_WRAPPERS.put(Void.TYPE, Void.class);
+    }
+}
+
diff --git a/kernel/src/java/org/gbean/kernel/ConstructorSignature.java b/kernel/src/java/org/gbean/kernel/ConstructorSignature.java
new file mode 100644
index 0000000..988cb7a
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/ConstructorSignature.java
@@ -0,0 +1,127 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.gbean.kernel;
+
+import java.lang.reflect.Constructor;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @version $Rev: 109957 $ $Date: 2004-12-05 23:52:06 -0800 (Sun, 05 Dec 2004) $
+ */
+public final class ConstructorSignature {
+    private final static String[] NO_TYPES = new String[0];
+    private final String[] parameterTypes;
+
+    public ConstructorSignature(Constructor constructor) {
+        Class[] parameters = constructor.getParameterTypes();
+        parameterTypes = new String[parameters.length];
+        for (int i = 0; i < parameters.length; i++) {
+            parameterTypes[i] = parameters[i].getName();
+        }
+    }
+
+    public ConstructorSignature(String[] argumentTypes) {
+        for (int i = 0; i < argumentTypes.length; i++) {
+            if (argumentTypes[i] == null) {
+                throw new IllegalArgumentException("Argument " + i + " is null");
+            }
+        }
+
+        if (argumentTypes != null) {
+            this.parameterTypes = argumentTypes;
+        } else {
+            this.parameterTypes = NO_TYPES;
+        }
+    }
+
+    public ConstructorSignature(Class[] argumentTypes) {
+        if (argumentTypes != null) {
+            this.parameterTypes = new String[argumentTypes.length];
+            for (int i = 0; i < argumentTypes.length; i++) {
+                this.parameterTypes[i] = argumentTypes[i].getName();
+            }
+        } else {
+            this.parameterTypes = NO_TYPES;
+        }
+    }
+
+    public ConstructorSignature(List argumentTypes) {
+        if (argumentTypes != null) {
+            this.parameterTypes = new String[argumentTypes.size()];
+            for (int i = 0; i < argumentTypes.size(); i++) {
+                Object argumentType = argumentTypes.get(i);
+                if (argumentType instanceof Class) {
+                    this.parameterTypes[i] = ((Class) argumentType).getName();
+                } else if (argumentType instanceof String) {
+                    this.parameterTypes[i] = (String) argumentType;
+                } else {
+                    throw new IllegalArgumentException("Argument type must be a String or a Class: index=" + i + ", type=" + argumentType.getClass());
+                }
+            }
+        } else {
+            this.parameterTypes = NO_TYPES;
+        }
+    }
+
+    public List getParameterTypes() {
+        return Collections.unmodifiableList(Arrays.asList(parameterTypes));
+    }
+
+    public boolean equals(Object object) {
+        if (!(object instanceof ConstructorSignature)) {
+            return false;
+        }
+
+        ConstructorSignature constructorSignature = (ConstructorSignature) object;
+
+        // match arg length
+        int length = constructorSignature.parameterTypes.length;
+        if (length != parameterTypes.length) {
+            return false;
+        }
+
+        // match each arg
+        for (int i = 0; i < length; i++) {
+            if (!constructorSignature.parameterTypes[i].equals(parameterTypes[i])) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public int hashCode() {
+        int result = 17;
+        for (int i = 0; i < parameterTypes.length; i++) {
+            result = 37 * result + parameterTypes[i].hashCode();
+        }
+        return result;
+    }
+
+    public String toString() {
+        StringBuffer buffer = new StringBuffer("<init>(");
+        for (int i = 0; i < parameterTypes.length; i++) {
+            if (i > 0) {
+                buffer.append(", ");
+            }
+            buffer.append(parameterTypes[i]);
+        }
+        return buffer.append(")").toString();
+    }
+}
diff --git a/kernel/src/java/org/gbean/kernel/DependencyManager.java b/kernel/src/java/org/gbean/kernel/DependencyManager.java
new file mode 100644
index 0000000..b21ff2e
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/DependencyManager.java
@@ -0,0 +1,123 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.gbean.kernel;
+
+import java.util.Collection;
+import java.util.Set;
+import javax.management.ObjectName;
+
+/**
+ * DependencyManager is the record keeper of the dependencies in the kernel.  The DependencyManager
+ * does not enforce any dependencies, it is simply a place where components can register their intent
+ * to be dependent on another component.
+ * <p/>
+ * The DependencyManager uses the nomenclature of parent-child where a child is dependent on a parent.
+ * The names parent and child have no other meaning are just a convience to make the code readable.
+ *
+ * @version $Rev: 169154 $ $Date: 2005-05-08 12:35:23 -0700 (Sun, 08 May 2005) $
+ */
+public interface DependencyManager {
+    /**
+     * Starts the dependency manager
+     */
+    public void start();
+
+    /**
+     * Stops the dependency manager releasing all resources
+     */
+    public void stop();
+
+    /**
+     * Declares a dependency from a child to a parent.
+     *
+     * @param child the dependent component
+     * @param parent the component the child is depending on
+     */
+    public void addDependency(ObjectName child, ObjectName parent);
+
+    /**
+     * Removes a dependency from a child to a parent
+     *
+     * @param child the dependnet component
+     * @param parent the component that the child wil no longer depend on
+     */
+    public void removeDependency(ObjectName child, ObjectName parent);
+
+    /**
+     * Removes all dependencies for a child
+     *
+     * @param child the component that will no longer depend on anything
+     */
+    public void removeAllDependencies(ObjectName child);
+
+    /**
+     * Adds dependencies from the child to every parent in the parents set
+     *
+     * @param child the dependent component
+     * @param parents the set of components the child is depending on
+     */
+    public void addDependencies(ObjectName child, Set parents);
+
+    /**
+     * Gets the set of parents that the child is depending on
+     *
+     * @param child the dependent component
+     * @return a collection containing all of the components the child depends on; will never be null
+     */
+    public Set getParents(ObjectName child);
+
+    /**
+     * Gets all of the services that have a dependency on the specified startParent.
+     *
+     * @param parent the component the returned childen set depend on
+     * @return a collection containing all of the components that depend on the parent; will never be null
+     */
+    public Set getChildren(ObjectName parent);
+
+    /**
+     * Adds a hold on a collection of object name patterns.  If the name of a component matches an object name
+     * pattern in the collection, the component should not start.
+     *
+     * @param objectName the name of the component placing the holds
+     * @param holds a collection of object name patterns which should not start
+     */
+    public void addStartHolds(ObjectName objectName, Collection holds);
+
+    /**
+     * Removes a collection of holds.
+     *
+     * @param objectName the object name of the components owning the holds
+     * @param holds a collection of the holds to remove
+     */
+    public void removeStartHolds(ObjectName objectName, Collection holds);
+
+    /**
+     * Removes all of the holds owned by a component.
+     *
+     * @param objectName the object name of the component that will no longer have any holds
+     */
+    public void removeAllStartHolds(ObjectName objectName);
+
+    /**
+     * Gets the object name of the bean blocking the start specified bean.
+     *
+     * @param objectName the bean to check for blockers
+     * @return the bean blocking the specified bean, or null if there are no blockers
+     */
+    public ObjectName checkBlocker(ObjectName objectName);
+}
diff --git a/kernel/src/java/org/gbean/kernel/Kernel.java b/kernel/src/java/org/gbean/kernel/Kernel.java
new file mode 100644
index 0000000..a719726
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/Kernel.java
@@ -0,0 +1,236 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.kernel;
+
+import java.util.Set;
+import java.util.Date;
+import java.util.Collection;
+import javax.management.ObjectName;
+
+import org.gbean.service.ServiceFactory;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public interface Kernel {
+    /**
+     * The name used by a Kernel to register itself when it boots.
+     */
+    ObjectName KERNEL = ServiceName.createName(":j2eeType=Kernel");
+
+    /**
+     * Get the name of this kernel
+     *
+     * @return the name of this kernel
+     */
+    String getKernelName();
+
+    /**
+     * Load a specific service into this kernel.
+     * This is intended for applications that are embedding the kernel.
+     *
+     * @param serviceFactory the service to load
+     * @param classLoader the class loader to use to load the service
+     * @throws ServiceAlreadyExistsException if the name is already used
+     */
+    void loadService(ObjectName name, ServiceFactory serviceFactory, ClassLoader classLoader) throws ServiceAlreadyExistsException;
+
+    /**
+     * Is there a service registered with the kernel under the specified name?
+     * @param name the name to check
+     * @return true if there is a service registered under the specified name; false otherwise
+     */
+    boolean isLoaded(ObjectName name);
+
+    /**
+     * Start a specific service.
+     *
+     * @param name the service to start
+     * @throws ServiceNotFoundException if the service could not be found
+     * @throws IllegalStateException If the service is disabled
+     */
+    void startService(ObjectName name) throws ServiceNotFoundException, IllegalStateException;
+
+    /**
+     * Start a specific service and its children.
+     *
+     * @param name the service to start
+     * @throws ServiceNotFoundException if the service could not be found
+     * @throws IllegalStateException If the service is disabled
+     */
+    void startRecursiveService(ObjectName name) throws ServiceNotFoundException, IllegalStateException;
+
+    /**
+     * Stop a specific service.
+     *
+     * @param name the service to stop
+     * @throws ServiceNotFoundException if the service could not be found
+     * @throws IllegalStateException If the service is disabled
+     */
+    void stopService(ObjectName name) throws ServiceNotFoundException, IllegalStateException;
+
+    /**
+     * Unload a specific service.
+     * This is intended for applications that are embedding the kernel.
+     *
+     * @param name the name of the service to unregister
+     * @throws ServiceNotFoundException if the service could not be found
+     */
+    void unloadService(ObjectName name) throws ServiceNotFoundException, IllegalStateException;
+
+    /**
+     * Gets a service instance.  This method should be use with extreme caution, as this method
+     * returns hard reference to the instance which if handled improperly will lead to memory
+     * leaks.
+     * @param name the name of the object to fetch
+     * @return the service instance
+     * @throws ServiceNotFoundException if the service is not loaded in to the kernel
+     * @throws IllegalStateException if the service is not in the RUNNING or STOPPING states
+     */
+    Object getService(ObjectName name) throws ServiceNotFoundException, IllegalStateException;
+
+    /**
+     * Gets the state of the specified service.
+     * @param name the name of the service
+     * @return the state of the service
+     * @throws ServiceNotFoundException if the service could not be found
+     */
+    int getServiceState(ObjectName name) throws ServiceNotFoundException;
+
+    /**
+     * Gets the time the specified service was started
+     * @param name the name of the service
+     * @return the start time of the service or 0 if not running
+     * @throws ServiceNotFoundException if the service could not be found
+     */
+    long getServiceStartTime(ObjectName name) throws ServiceNotFoundException;
+
+    /**
+     * Is the specified service enabled?
+     * @param name the name if the service
+     * @return true if the service is enabled
+     * @throws ServiceNotFoundException if the service could not be found
+     */
+    boolean isServiceEnabled(ObjectName name) throws ServiceNotFoundException;
+
+    /**
+     * Sets the eneabled status of the specified service.  A disabled service can not be started, and
+     * will not be started via startRecursive.
+     * @param name the name if the service
+     * @param enabled the new enabled status
+     * @throws ServiceNotFoundException if the service could not be found
+     */
+    void setServiceEnabled(ObjectName name, boolean enabled) throws ServiceNotFoundException;
+
+    /**
+     * Gets the ClassLoader used to register the specified service
+     * @param name the name of the service from which the class loader should be extracted
+     * @return the class loader associated with the specified service
+     * @throws ServiceNotFoundException if the specified service is not registered with the kernel
+     */
+    ClassLoader getClassLoaderFor(ObjectName name) throws ServiceNotFoundException;
+
+    /**
+     * Return the ServiceFactory for a registered service instance.
+     * @param name the name of the service whose info should be returned
+     * @return the info for that instance
+     * @throws ServiceNotFoundException if there is no instance with the supplied name
+     */
+    ServiceFactory getServiceFactory(ObjectName name) throws ServiceNotFoundException;
+
+    /**
+     * Returns a Set of all services matching the object name pattern
+     * @return a List of the matching services registered with this kernel
+     */
+    Collection listServices(ObjectName pattern);
+
+    /**
+     * Returns a Set of all services matching the set of object name pattern
+     * @return a List of the matching services registered with this kernel
+     */
+    Set listServices(Set patterns);
+
+    /**
+     * Returns a Set of all services matching the object name pattern
+     * @return a List of javax.management.ObjectName of matching services registered with this kernel
+     */
+    Set listServiceNames(ObjectName pattern);
+
+    /**
+     * Returns a Set of all services matching the set of object name pattern
+     * @return a List of javax.management.ObjectName of matching services registered with this kernel
+     */
+    Set listServiceNames(Set patterns);
+
+    /**
+     * Brings the kernel online
+     * @throws Exception if the kernel can not boot
+     */
+    void boot() throws Exception;
+
+    /**
+     * Returns the time this kernel was last booted.
+     * @return the time this kernel was last booted; null if the kernel has not been
+     */
+    Date getBootTime();
+
+    /**
+     * Registers a runnable to execute when the kernel is shutdown
+     * @param hook a runnable to execute when the kernel is shutdown
+     */
+    void registerShutdownHook(Runnable hook);
+
+    /**
+     * Unregisters a runnable from the list to execute when the kernel is shutdown
+     * @param hook the runnable that should be removed
+     */
+    void unregisterShutdownHook(Runnable hook);
+
+    /**
+     * Stops the kernel
+     */
+    void shutdown();
+
+    /**
+     * Has the kernel been booted
+     * @return true if the kernel has been booted; false otherwise
+     */
+    boolean isRunning();
+
+    /**
+     * Adds a listener for all lifecycle events any service matching the pattern
+     *
+     * This is equivalent to addLifecycleListener(lifecycleListener, Collections.singleton(pattern))
+     *
+     * @param lifecycleListener the listener instance
+     * @param pattern the pattern used to filter events
+     */
+    void addLifecycleListener(LifecycleListener lifecycleListener, ObjectName pattern);
+
+    /**
+     * Registers a listener to revieve life cycle events for a set of object name patterns.
+     * @param lifecycleListener the listener that will receive life cycle events
+     * @param patterns a set of ObjectName patterns
+     */
+    void addLifecycleListener(LifecycleListener lifecycleListener, Set patterns);
+
+    /**
+     * Removes the listener from all notifications.
+     * @param lifecycleListener the listener to unregister
+     */
+    void removeLifecycleListener(LifecycleListener lifecycleListener);
+}
diff --git a/kernel/src/java/org/gbean/kernel/KernelException.java b/kernel/src/java/org/gbean/kernel/KernelException.java
new file mode 100644
index 0000000..faa640c
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/KernelException.java
@@ -0,0 +1,37 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.kernel;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class KernelException extends Exception {
+    public KernelException() {
+    }
+
+    public KernelException(String message) {
+        super(message);
+    }
+
+    public KernelException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public KernelException(Throwable cause) {
+        super(cause);
+    }
+}
diff --git a/kernel/src/java/org/gbean/kernel/KernelFactory.java b/kernel/src/java/org/gbean/kernel/KernelFactory.java
new file mode 100644
index 0000000..fa8ccd4
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/KernelFactory.java
@@ -0,0 +1,89 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.kernel;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import org.gbean.kernel.simple.SimpleKernelFactory;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public abstract class KernelFactory {
+    public static final String KERNEL_FACTORY_KEY = KernelFactory.class.getName();
+
+    public static KernelFactory newInstance() {
+        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+        if (classLoader == null) {
+            classLoader = KernelFactory.class.getClassLoader();
+        }
+
+        // System property
+        try {
+            String kernelFactoryName = System.getProperty(KERNEL_FACTORY_KEY);
+            if (kernelFactoryName != null) {
+                return createKernelFactory(kernelFactoryName, classLoader);
+            }
+        } catch (SecurityException se) {
+        }
+
+        // Jar Service Specification - http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html
+        String serviceId = "META-INF/services/" + KERNEL_FACTORY_KEY;
+        InputStream inputStream = null;
+        try {
+            inputStream = classLoader.getResourceAsStream(serviceId);
+            if (inputStream != null) {
+                BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
+                String kernelFactoryName = reader.readLine();
+                reader.close();
+
+                if (kernelFactoryName != null && kernelFactoryName.length() > 0) {
+                    return createKernelFactory(kernelFactoryName, classLoader);
+                }
+            }
+        } catch (Exception ignored) {
+        } finally {
+            if (inputStream != null) {
+                try {
+                    inputStream.close();
+                } catch (IOException ignored) {
+                }
+                inputStream = null;
+            }
+        }
+
+        // Default is the basic kernel
+        return new SimpleKernelFactory();
+    }
+
+    private static KernelFactory createKernelFactory(String className, ClassLoader classLoader) {
+        try {
+            return (KernelFactory) classLoader.loadClass(className).newInstance();
+        } catch (ClassCastException e) {
+            throw new KernelFactoryError("Kernel factory class does not implement KernelFactory: " + className);
+        } catch (ClassNotFoundException e) {
+            throw new KernelFactoryError("Kernel factory class not found: " + className);
+        } catch (Exception e) {
+            throw new KernelFactoryError("Unable to instantiate kernel factory class: " + className, e);
+        }
+    }
+
+    public abstract Kernel createKernel(String kernelName);
+}
diff --git a/kernel/src/java/org/gbean/kernel/KernelFactoryError.java b/kernel/src/java/org/gbean/kernel/KernelFactoryError.java
new file mode 100644
index 0000000..4ca3aa0
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/KernelFactoryError.java
@@ -0,0 +1,38 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.kernel;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class KernelFactoryError extends Error {
+    public KernelFactoryError() {
+        super();
+    }
+
+    public KernelFactoryError(String message) {
+        super(message);
+    }
+
+    public KernelFactoryError(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public KernelFactoryError(Throwable cause) {
+        super(cause);
+    }
+}
diff --git a/kernel/src/java/org/gbean/kernel/KernelMain.java b/kernel/src/java/org/gbean/kernel/KernelMain.java
new file mode 100644
index 0000000..77d87d6
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/KernelMain.java
@@ -0,0 +1,200 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.kernel;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import javax.management.ObjectName;
+
+import org.gbean.kernel.simple.SimpleServiceFactory;
+import org.gbean.loader.LoaderUtil;
+import org.gbean.spring.FatalStartupError;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class KernelMain implements Main {
+    private static final String DEFAULT_KERNEL_NAME = "gbean";
+
+    private Kernel kernel;
+    private ClassLoader classLoader;
+    private Map services = Collections.EMPTY_MAP;
+    private List locations = Collections.EMPTY_LIST;
+    private boolean daemon = true;
+    private Main next;
+
+    public Kernel getKernel() {
+        return kernel;
+    }
+
+    public void setKernel(Kernel kernel) {
+        this.kernel = kernel;
+    }
+
+    public ClassLoader getClassLoader() {
+        return classLoader;
+    }
+
+    public void setClassLoader(ClassLoader classLoader) {
+        this.classLoader = classLoader;
+    }
+
+    public Map getServices() {
+        return services;
+    }
+
+    public void setServices(Map services) {
+        this.services = services;
+    }
+
+    public List getLocations() {
+        return locations;
+    }
+
+    public void setLocations(List locations) {
+        this.locations = locations;
+    }
+
+    public boolean isDaemon() {
+        return daemon;
+    }
+
+    public void setDaemon(boolean daemon) {
+        this.daemon = daemon;
+    }
+
+    public Main getNext() {
+        return next;
+    }
+
+    public void setNext(Main next) {
+        this.next = next;
+    }
+
+    public void main(String[] args) {
+        if (classLoader == null) {
+            classLoader = Thread.currentThread().getContextClassLoader();
+        }
+
+        ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
+        Thread.currentThread().setContextClassLoader(classLoader);
+        try {
+            // create a default kernel if necessary
+            if (kernel == null) {
+                kernel = KernelFactory.newInstance().createKernel(DEFAULT_KERNEL_NAME);
+            }
+
+            // boot the kernel
+            try {
+                kernel.boot();
+            } catch (Exception e) {
+                throw new FatalStartupError("Unable to boot the kernel", e);
+            }
+
+            // add our shutdown hook
+            kernel.registerShutdownHook(new Runnable() {
+                public void run() {
+                    // bind the bootstrap services
+                    for (Iterator iterator = services.keySet().iterator(); iterator.hasNext();) {
+                        String name = (String) iterator.next();
+                        try {
+                            ObjectName objectName = new ObjectName(name);
+                            kernel.stopService(objectName);
+                            kernel.unloadService(objectName);
+                        } catch (Exception e) {
+                            // igore -- a real exception will be logged by the kernel
+                        }
+                    }
+                }
+            });
+
+            boolean failed = false;
+            try {
+                // bind the bootstrap services
+                for (Iterator iterator = services.entrySet().iterator(); iterator.hasNext();) {
+                    Map.Entry entry = (Map.Entry) iterator.next();
+                    String name = (String) entry.getKey();
+                    Object service = entry.getValue();
+
+                    try {
+                        ObjectName objectName = new ObjectName(name);
+                        kernel.loadService(objectName, new SimpleServiceFactory(service), classLoader);
+                        kernel.startService(objectName);
+                    } catch (Exception e) {
+                        throw new FatalStartupError("Unable to bind bootstrap service '" + name + "' into the kernel", e);
+                    }
+                }
+
+                // verify that all bootstrap services started successfully
+                LoaderUtil.verifyAllServicesRunning(kernel);
+
+
+                // load each location and verify that all services started successfully
+                for (Iterator iterator = locations.iterator(); iterator.hasNext();) {
+                    String location = (String) iterator.next();
+                    LoaderUtil.load(kernel, location);
+                    LoaderUtil.verifyAllServicesRunning(kernel);
+                }
+
+                // if we have a child main class call it
+                if (next != null) {
+                    next.main(args);
+                }
+
+                // if we are a daemon we wait here until the server stops
+                if (daemon) {
+                    // add our shutdown hook
+                    Runtime.getRuntime().addShutdownHook(new Thread("Shutdown Thread") {
+                        public void run() {
+                            kernel.shutdown();
+                        }
+                    });
+
+                    while (kernel.isRunning()) {
+                        try {
+                            // wait for the kernel to be ready to exit
+                            synchronized (kernel) {
+                                kernel.wait();
+                            }
+                        } catch (InterruptedException e) {
+                            // ignore - we check the variable above
+                        }
+                    }
+                }
+            } catch (RuntimeException e) {
+                failed = true;
+                throw e;
+            } catch (Error e) {
+                failed = true;
+                throw e;
+            } finally {
+                try {
+                    kernel.shutdown();
+                } catch (Exception e) {
+                    // if we are not alredy throwing an exception, throw a new exception
+                    if (!failed) {
+                        throw new FatalStartupError("Exception while shutting down kernel", e);
+                    }
+                }
+            }
+        } finally {
+            Thread.currentThread().setContextClassLoader(oldClassLoader);
+        }
+    }
+}
diff --git a/kernel/src/java/org/gbean/kernel/KernelRegistry.java b/kernel/src/java/org/gbean/kernel/KernelRegistry.java
new file mode 100644
index 0000000..42895cf
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/KernelRegistry.java
@@ -0,0 +1,124 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.kernel;
+
+import java.lang.ref.WeakReference;
+import java.lang.ref.ReferenceQueue;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.Collections;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public final class KernelRegistry {
+    /**
+     * Index of kernel references by kernel name
+     */
+    private static final Map kernels = new HashMap();
+
+    /**
+     * ReferenceQueue that watches the weak references to our kernels
+     */
+    private static final ReferenceQueue queue = new ReferenceQueue();
+
+    public static Set getKernelNames() {
+        synchronized(kernels) {
+            return Collections.unmodifiableSet(kernels.keySet());
+        }
+    }
+
+    /**
+     * Get a particular kernel indexed by a name
+     *
+     * @param name the name of the kernel to be obtained
+     * @return the kernel that was registered with that name
+     */
+    public static Kernel getKernel(String name) {
+        if (name == null) {
+            return getSingleKernel();
+        }
+        synchronized (kernels) {
+            processQueue();
+            KernelReference ref = (KernelReference) kernels.get(name);
+            if (ref != null) {
+                return (Kernel) ref.get();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Obtain the single kernel that's registered.
+     * <p/>
+     * <p>This method assumes that there is only one kernel registered and will throw an
+     * <code>IllegalStateException</code> if more than one has been registered.
+     *
+     * @return the single kernel that's registered
+     * @throws IllegalStateException if more than one
+     */
+    public static Kernel getSingleKernel() {
+        synchronized (kernels) {
+            processQueue();
+
+            int size = kernels.size();
+            if (size > 1) throw new IllegalStateException("More than one kernel has been registered.");
+            if (size < 1) return null;
+
+            Kernel result = (Kernel) ((KernelReference) kernels.values().iterator().next()).get();
+            if (result == null) {
+                kernels.clear();
+            }
+            return result;
+        }
+    }
+
+    public static void registerKernel(Kernel kernel) {
+        synchronized (kernels) {
+            String kernelName = kernel.getKernelName();
+            if (kernels.containsKey(kernelName)) {
+                throw new IllegalStateException("A kernel is already running this kernel name: " + kernelName);
+            }
+            kernels.put(kernelName, new KernelReference(kernelName, kernel));
+        }
+    }
+
+    public static void unregisterKernel(Kernel kernel) {
+        synchronized (kernels) {
+            kernels.remove(kernel.getKernelName());
+        }
+    }
+
+    private static void processQueue() {
+        KernelReference kernelRef;
+        while ((kernelRef = (KernelReference) queue.poll()) != null) {
+            synchronized (kernels) {
+                kernels.remove(kernelRef.key);
+            }
+        }
+    }
+
+    private static class KernelReference extends WeakReference {
+        private final Object key;
+
+        public KernelReference(Object key, Object kernel) {
+            super(kernel, queue);
+            this.key = key;
+        }
+    }
+}
diff --git a/kernel/src/java/org/gbean/kernel/KernelUtil.java b/kernel/src/java/org/gbean/kernel/KernelUtil.java
new file mode 100644
index 0000000..d29438c
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/KernelUtil.java
@@ -0,0 +1,62 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.kernel;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.Collections;
+import javax.management.ObjectName;
+
+import org.gbean.kernel.runtime.ServiceState;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class KernelUtil {
+    public static Set getRunningServiceNames(Kernel kernel, Set patterns) {
+        Set runningTargets = new HashSet();
+        Set services = kernel.listServiceNames(patterns);
+        for (Iterator iterator = services.iterator(); iterator.hasNext();) {
+            ObjectName objectName = (ObjectName) iterator.next();
+            if (isRunning(kernel, objectName)) {
+                runningTargets.add(objectName);
+            }
+        }
+        return runningTargets;
+    }
+
+    public static Set getRunningServiceNames(Kernel kernel, ObjectName pattern) {
+        return getRunningServiceNames(kernel, Collections.singleton(pattern));
+    }
+
+    /**
+     * Is the component in the Running state
+     *
+     * @param objectName name of the component to check
+     * @return true if the component is running; false otherwise
+     */
+    public static boolean isRunning(Kernel kernel, ObjectName objectName) {
+        try {
+            int state = kernel.getServiceState(objectName);
+            return state == ServiceState.RUNNING_INDEX;
+        } catch (ServiceNotFoundException e) {
+            // service is no longer registerd
+            return false;
+        }
+    }
+}
diff --git a/kernel/src/java/org/gbean/kernel/LifecycleAdapter.java b/kernel/src/java/org/gbean/kernel/LifecycleAdapter.java
new file mode 100644
index 0000000..63d3d23
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/LifecycleAdapter.java
@@ -0,0 +1,42 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.kernel;
+
+import javax.management.ObjectName;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class LifecycleAdapter implements LifecycleListener {
+    public void loaded(ObjectName objectName) {
+    }
+
+    public void starting(ObjectName objectName) {
+    }
+
+    public void running(ObjectName objectName) {
+    }
+
+    public void stopping(ObjectName objectName) {
+    }
+
+    public void stopped(ObjectName objectName) {
+    }
+
+    public void unloaded(ObjectName objectName) {
+    }
+}
diff --git a/kernel/src/java/org/gbean/kernel/LifecycleListener.java b/kernel/src/java/org/gbean/kernel/LifecycleListener.java
new file mode 100644
index 0000000..93aa6d7
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/LifecycleListener.java
@@ -0,0 +1,32 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.kernel;
+
+import java.util.EventListener;
+import javax.management.ObjectName;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public interface LifecycleListener extends EventListener {
+    public void loaded(ObjectName objectName);
+    public void starting(ObjectName objectName);
+    public void running(ObjectName objectName);
+    public void stopping(ObjectName objectName);
+    public void stopped(ObjectName objectName);
+    public void unloaded(ObjectName objectName);
+}
diff --git a/kernel/src/java/org/gbean/kernel/Main.java b/kernel/src/java/org/gbean/kernel/Main.java
new file mode 100644
index 0000000..ffa92c9
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/Main.java
@@ -0,0 +1,24 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.kernel;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public interface Main {
+    void main(String[]  args);
+}
diff --git a/kernel/src/java/org/gbean/kernel/MalformedServiceNameException.java b/kernel/src/java/org/gbean/kernel/MalformedServiceNameException.java
new file mode 100644
index 0000000..fc8dc05
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/MalformedServiceNameException.java
@@ -0,0 +1,33 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.kernel;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class MalformedServiceNameException extends RuntimeException {
+    private final String name;
+
+    public MalformedServiceNameException(String name) {
+        super (name);
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+}
diff --git a/kernel/src/java/org/gbean/kernel/NoSuchAttributeException.java b/kernel/src/java/org/gbean/kernel/NoSuchAttributeException.java
new file mode 100644
index 0000000..4679895
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/NoSuchAttributeException.java
@@ -0,0 +1,37 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.kernel;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class NoSuchAttributeException extends KernelException {
+    public NoSuchAttributeException() {
+    }
+
+    public NoSuchAttributeException(String message) {
+        super(message);
+    }
+
+    public NoSuchAttributeException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public NoSuchAttributeException(Throwable cause) {
+        super(cause);
+    }
+}
diff --git a/kernel/src/java/org/gbean/kernel/NoSuchOperationException.java b/kernel/src/java/org/gbean/kernel/NoSuchOperationException.java
new file mode 100644
index 0000000..52336ec
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/NoSuchOperationException.java
@@ -0,0 +1,37 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.kernel;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class NoSuchOperationException extends KernelException {
+    public NoSuchOperationException() {
+    }
+
+    public NoSuchOperationException(String message) {
+        super(message);
+    }
+
+    public NoSuchOperationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public NoSuchOperationException(Throwable cause) {
+        super(cause);
+    }
+}
diff --git a/kernel/src/java/org/gbean/kernel/OperationSignature.java b/kernel/src/java/org/gbean/kernel/OperationSignature.java
new file mode 100644
index 0000000..efdf963
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/OperationSignature.java
@@ -0,0 +1,135 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.gbean.kernel;
+
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Arrays;
+import java.util.Collections;
+
+/**
+ * @version $Rev: 109957 $ $Date: 2004-12-05 23:52:06 -0800 (Sun, 05 Dec 2004) $
+ */
+public final class OperationSignature {
+    private final static String[] NO_TYPES = new String[0];
+    private final String name;
+    private final String[] parameterTypes;
+
+    public OperationSignature(Method method) {
+        name = method.getName();
+        Class[] parameters = method.getParameterTypes();
+        parameterTypes = new String[parameters.length];
+        for (int i = 0; i < parameters.length; i++) {
+            parameterTypes[i] = parameters[i].getName();
+        }
+    }
+
+    public OperationSignature(String name, String[] argumentTypes) {
+        this.name = name;
+        if (argumentTypes != null) {
+            this.parameterTypes = argumentTypes;
+        } else {
+            this.parameterTypes = NO_TYPES;
+        }
+    }
+
+    public OperationSignature(String name, Class[] argumentTypes) {
+        this.name = name;
+        if (argumentTypes != null) {
+            this.parameterTypes = new String[argumentTypes.length];
+            for (int i = 0; i < argumentTypes.length; i++) {
+                this.parameterTypes[i] = argumentTypes[i].getName();
+            }
+        } else {
+            this.parameterTypes = NO_TYPES;
+        }
+    }
+
+    public OperationSignature(String name, List argumentTypes) {
+        this.name = name;
+        if (argumentTypes != null) {
+            this.parameterTypes = new String[argumentTypes.size()];
+            for (int i = 0; i < argumentTypes.size(); i++) {
+                Object argumentType = argumentTypes.get(i);
+                if (argumentType instanceof Class) {
+                    this.parameterTypes[i] = ((Class) argumentType).getName();
+                } else if (argumentType instanceof String) {
+                    this.parameterTypes[i] = (String) argumentType;
+                } else {
+                    throw new IllegalArgumentException("Argument type must be a String or a Class: index=" + i + ", type=" + argumentType.getClass());
+                }
+            }
+        } else {
+            this.parameterTypes = NO_TYPES;
+        }
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public List getParameterTypes() {
+        return Collections.unmodifiableList(Arrays.asList(parameterTypes));
+    }
+
+    public boolean equals(Object object) {
+        if (!(object instanceof OperationSignature)) {
+            return false;
+        }
+
+        // match names
+        OperationSignature methodKey = (OperationSignature) object;
+        if (!methodKey.name.equals(name)) {
+            return false;
+        }
+
+        // match arg length
+        int length = methodKey.parameterTypes.length;
+        if (length != parameterTypes.length) {
+            return false;
+        }
+
+        // match each arg
+        for (int i = 0; i < length; i++) {
+            if (!methodKey.parameterTypes[i].equals(parameterTypes[i])) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public int hashCode() {
+        int result = 17;
+        result = 37 * result + name.hashCode();
+        for (int i = 0; i < parameterTypes.length; i++) {
+            result = 37 * result + parameterTypes[i].hashCode();
+        }
+        return result;
+    }
+
+    public String toString() {
+        StringBuffer buffer = new StringBuffer(name).append("(");
+        for (int i = 0; i < parameterTypes.length; i++) {
+            if (i > 0) {
+                buffer.append(", ");
+            }
+            buffer.append(parameterTypes[i]);
+        }
+        return buffer.append(")").toString();
+    }
+}
diff --git a/kernel/src/java/org/gbean/kernel/ServiceAlreadyExistsException.java b/kernel/src/java/org/gbean/kernel/ServiceAlreadyExistsException.java
new file mode 100644
index 0000000..83efc0a
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/ServiceAlreadyExistsException.java
@@ -0,0 +1,37 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.kernel;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class ServiceAlreadyExistsException extends KernelException {
+    public ServiceAlreadyExistsException() {
+    }
+
+    public ServiceAlreadyExistsException(String message) {
+        super(message);
+    }
+
+    public ServiceAlreadyExistsException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public ServiceAlreadyExistsException(Throwable cause) {
+        super(cause);
+    }
+}
diff --git a/kernel/src/java/org/gbean/kernel/ServiceName.java b/kernel/src/java/org/gbean/kernel/ServiceName.java
new file mode 100644
index 0000000..20a06ce
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/ServiceName.java
@@ -0,0 +1,46 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.kernel;
+
+import java.util.Properties;
+import javax.management.ObjectName;
+import javax.management.MalformedObjectNameException;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public final class ServiceName {
+    private ServiceName() {
+    }
+
+    public static ObjectName createName(String name) {
+        try {
+            return new ObjectName(name);
+        } catch (MalformedObjectNameException e) {
+            throw new MalformedServiceNameException(name);
+        }
+    }
+
+    public static ObjectName createName(String domainName, Properties properties) {
+        try {
+            return new ObjectName(domainName, properties);
+        } catch (MalformedObjectNameException e) {
+            // todo more descriptive message here
+            throw new MalformedServiceNameException("");
+        }
+    }
+}
diff --git a/kernel/src/java/org/gbean/kernel/ServiceNotFoundException.java b/kernel/src/java/org/gbean/kernel/ServiceNotFoundException.java
new file mode 100644
index 0000000..73797a9
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/ServiceNotFoundException.java
@@ -0,0 +1,37 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.kernel;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class ServiceNotFoundException extends KernelException {
+    public ServiceNotFoundException() {
+    }
+
+    public ServiceNotFoundException(String message) {
+        super(message);
+    }
+
+    public ServiceNotFoundException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public ServiceNotFoundException(Throwable cause) {
+        super(cause);
+    }
+}
diff --git a/kernel/src/java/org/gbean/kernel/runtime/LifecycleBroadcaster.java b/kernel/src/java/org/gbean/kernel/runtime/LifecycleBroadcaster.java
new file mode 100644
index 0000000..a484b18
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/runtime/LifecycleBroadcaster.java
@@ -0,0 +1,31 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.kernel.runtime;
+
+
+
+/**
+ * @version $Rev$ $Date$
+ */
+public interface LifecycleBroadcaster {
+    public void fireLoadedEvent();
+    public void fireStartingEvent();
+    public void fireRunningEvent();
+    public void fireStoppingEvent();
+    public void fireStoppedEvent();
+    public void fireUnloadedEvent();
+}
diff --git a/kernel/src/java/org/gbean/kernel/runtime/ServiceDependency.java b/kernel/src/java/org/gbean/kernel/runtime/ServiceDependency.java
new file mode 100644
index 0000000..2b546e5
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/runtime/ServiceDependency.java
@@ -0,0 +1,217 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.gbean.kernel.runtime;
+
+import java.util.Iterator;
+import java.util.Set;
+import javax.management.ObjectName;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.gbean.kernel.Kernel;
+import org.gbean.kernel.DependencyManager;
+import org.gbean.kernel.LifecycleListener;
+import org.gbean.kernel.LifecycleAdapter;
+import org.gbean.kernel.KernelUtil;
+
+/**
+ * @version $Rev: 71492 $ $Date: 2004-11-14 21:31:50 -0800 (Sun, 14 Nov 2004) $
+ */
+public class ServiceDependency {
+    private static final Log log = LogFactory.getLog(ServiceDependency.class);
+
+    /**
+     * The ServiceInstance to which this reference belongs.
+     */
+    private final ServiceInstance serviceInstance;
+
+    /**
+     * The target objectName patterns to watch for a connection.
+     */
+    private final Set patterns;
+
+    /**
+     * Descriptive name of dependency
+     */
+    private final String name;
+
+    /**
+     * The kernel to which the reference is bound.
+     */
+    private final Kernel kernel;
+
+    /**
+     * The dependency manager of the kernel.
+     */
+    private final DependencyManager dependencyManager;
+
+    /**
+     * Is the ServiceInstance waitng for me to start?
+     */
+    private boolean waiting = false;
+
+    /**
+     * The object to which the proxy is bound
+     */
+    private ObjectName proxyTarget;
+
+    /**
+     * Our listener for lifecycle events
+     */
+    private LifecycleListener listener;
+
+    /**
+     * Why is the dependency in the current state
+     */
+    private String statusDescription;
+
+    public ServiceDependency(ServiceInstance serviceInstance, String name, Set patterns, Kernel kernel, DependencyManager dependencyManager) {
+        this.serviceInstance = serviceInstance;
+        this.name = name;
+        this.patterns = patterns;
+        this.kernel = kernel;
+        this.dependencyManager = dependencyManager;
+        statusDescription = "NOT STARTED: " + getDescription();
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Set getPatterns() {
+        return patterns;
+    }
+
+    public boolean isWaiting() {
+        return waiting;
+    }
+
+    public String getStatusDescription() {
+        return statusDescription;
+    }
+
+    public synchronized boolean start() {
+        // We only need to start if there are patterns and we don't already have a proxy
+        if (proxyTarget == null) {
+            if (listener == null) {
+                listener = new DependencyLifecycleListener();
+                try {
+                    kernel.addLifecycleListener(listener, patterns);
+                } catch (Error e) {
+                    throw e;
+                } catch (RuntimeException e) {
+                    throw e;
+                }
+            }
+
+            //
+            // We must have exactally one running target
+            //
+            ObjectName objectName = serviceInstance.getObjectName();
+            Set services = KernelUtil.getRunningServiceNames(kernel, patterns);
+            if (services.size() == 0) {
+                waiting = true;
+                statusDescription = "WAITING: no targest " + getDescription() + ", patterns=" + getPatternsText();
+                log.debug(statusDescription);
+                return false;
+            } else if (services.size() > 1) {
+                waiting = true;
+                statusDescription = "WAITING: to many targets " + getDescription() + ", patterns=" + getPatternsText();
+                log.debug(statusDescription);
+                return false;
+            }
+
+            //
+            // ready to start
+            //
+            waiting = false;
+
+            // stop all services that would match our patterns from starting
+            dependencyManager.addStartHolds(objectName, patterns);
+
+            // add a dependency on our target
+            proxyTarget = (ObjectName) services.iterator().next();
+            dependencyManager.addDependency(objectName, proxyTarget);
+
+            statusDescription = "READY: " + getDescription();
+        }
+
+        return true;
+    }
+
+    public synchronized void stop() {
+        waiting = false;
+        ObjectName objectName = serviceInstance.getObjectName();
+        dependencyManager.removeStartHolds(objectName, patterns);
+
+        if (proxyTarget != null) {
+            dependencyManager.removeDependency(objectName, proxyTarget);
+            proxyTarget = null;
+        }
+
+        if (listener != null) {
+            kernel.removeLifecycleListener(listener);
+            listener = null;
+        }
+
+        statusDescription = "STOPPED: " + getDescription();
+    }
+
+    private synchronized void checkStatus() {
+        Set services = KernelUtil.getRunningServiceNames(kernel, patterns);
+
+        // if we are running, and we now have two valid targets, which is an illegal state so we need to fail
+        // todo fix me
+        if (serviceInstance.getState() == ServiceState.RUNNING_INDEX && services.size() != 1) {
+            serviceInstance.stop();
+        } else if (waiting) {
+            if (services.size() == 1) {
+                // the service was waiting for me and not there is now just one target
+                waiting = false;
+                serviceInstance.start();
+            }
+        }
+    }
+
+    private String getPatternsText() {
+        StringBuffer buf = new StringBuffer();
+        for (Iterator iterator = patterns.iterator(); iterator.hasNext();) {
+            ObjectName objectName = (ObjectName) iterator.next();
+            buf.append(objectName.getCanonicalName()).append(" ");
+        }
+        return buf.toString();
+    }
+
+    private String getDescription() {
+        return "depependency=" + name + ", serviceInstance " + serviceInstance.getObjectName().getCanonicalName();
+    }
+
+    private class DependencyLifecycleListener extends LifecycleAdapter {
+        public void running(ObjectName objectName) {
+            checkStatus();
+        }
+
+        public void stopped(ObjectName objectName) {
+            checkStatus();
+        }
+
+        public void unloaded(ObjectName objectName) {
+            checkStatus();
+        }
+    }
+}
diff --git a/kernel/src/java/org/gbean/kernel/runtime/ServiceInstance.java b/kernel/src/java/org/gbean/kernel/runtime/ServiceInstance.java
new file mode 100644
index 0000000..4f7b2a6
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/runtime/ServiceInstance.java
@@ -0,0 +1,432 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.gbean.kernel.runtime;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import javax.management.ObjectName;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.gbean.kernel.DependencyManager;
+import org.gbean.kernel.Kernel;
+import org.gbean.kernel.ServiceNotFoundException;
+import org.gbean.service.ServiceContext;
+import org.gbean.service.ServiceFactory;
+
+/**
+ * @version $Rev: 106387 $ $Date: 2004-11-23 22:16:54 -0800 (Tue, 23 Nov 2004) $
+ */
+public final class ServiceInstance {
+    private static final Log log = LogFactory.getLog(ServiceInstance.class);
+
+    private static final int DESTROYED = 0;
+    private static final int CREATING = 1;
+    private static final int RUNNING = 2;
+    private static final int DESTROYING = 3;
+
+    /**
+     * The kernel in which this server is registered.
+     */
+    private final Kernel kernel;
+
+    /**
+     * The factory used to create the actual object instance.
+     */
+    private ServiceFactory serviceFactory;
+
+    /**
+     * This handles all state transiitions for this instance.
+     */
+    private final ServiceInstanceState serviceInstanceState;
+
+    /**
+     * The single listener to which we broadcast lifecycle change events.
+     */
+    private final LifecycleBroadcaster lifecycleBroadcaster;
+
+    /**
+     * The context given to the instance
+     */
+    private final ServiceContext serviceContext;
+
+    /**
+     * The classloader used for all invocations and creating targets.
+     */
+    private final ClassLoader classLoader;
+
+    /**
+     * Has this instance been destroyed?
+     */
+    private boolean dead = false;
+
+    /**
+     * The state of the internal service instance that we are wrapping.
+     */
+    private int instanceState = DESTROYED;
+
+    /**
+     * Target instance of this service instance
+     */
+    private Object target;
+
+    /**
+     * The time this application started.
+     */
+    private long startTime;
+
+    private Set dependencies;
+    private final ObjectName objectName;
+
+    public ServiceInstance(ObjectName objectName,
+            ServiceFactory serviceFactory,
+            Kernel kernel,
+            DependencyManager dependencyManager,
+            LifecycleBroadcaster lifecycleBroadcaster,
+            ClassLoader classLoader) {
+
+        this.objectName = objectName;
+        this.serviceFactory = serviceFactory;
+
+        // add the dependencies
+        Set tempDependencies = new HashSet();
+        for (Iterator iterator = serviceFactory.getDependencies().entrySet().iterator(); iterator.hasNext();) {
+            Map.Entry entry = (Map.Entry) iterator.next();
+            String dependencyName = (String) entry.getKey();
+            Set patterns = (Set) entry.getValue();
+            tempDependencies.add(new ServiceDependency(this, dependencyName, patterns, kernel, dependencyManager));
+        }
+        this.dependencies = Collections.unmodifiableSet(tempDependencies);
+
+        this.kernel = kernel;
+        this.lifecycleBroadcaster = lifecycleBroadcaster;
+        this.serviceInstanceState = new ServiceInstanceState(objectName, kernel, dependencyManager, this, lifecycleBroadcaster);
+        this.classLoader = classLoader;
+
+        serviceContext = new ServiceInstanceContext(this);
+    }
+
+    public void init() {
+        lifecycleBroadcaster.fireLoadedEvent();
+    }
+
+    public void destroy() throws ServiceNotFoundException {
+        synchronized (this) {
+            if (dead) {
+                // someone beat us to the punch... this instance should have never been found in the first place
+                throw new ServiceNotFoundException(objectName.getCanonicalName());
+            }
+            dead = true;
+        }
+
+        // if the bean is already stopped or failed, this will do nothing; otherwise it will shutdown the bean
+        serviceInstanceState.stop();
+
+        // tell everyone we are done
+        lifecycleBroadcaster.fireUnloadedEvent();
+    }
+
+    /**
+     * The kernel in which this instance is mounted
+     * @return the kernel
+     */
+    public Kernel getKernel() {
+        return kernel;
+    }
+
+    /**
+     * The class loader used to build this service.  This class loader is set into the thread context
+     * class loader before callint the target instace.
+     *
+     * @return the class loader used to build this service
+     */
+    public ClassLoader getClassLoader() {
+        return classLoader;
+    }
+
+    public synchronized Object getInstance() {
+        return target;
+    }
+
+    /**
+     * Has this service instance been destroyed. An destroyed service can no longer be used.
+     *
+     * @return true if the service has been destroyed
+     */
+    public synchronized boolean isDead() {
+        return dead;
+    }
+
+    public final ObjectName getObjectName() {
+        return objectName;
+    }
+
+    public ServiceFactory getServiceFactory() {
+        return serviceFactory;
+    }
+
+    /**
+     * Is this service enabled.  A disabled service can not be started.
+     *
+     * @return true if the service is enabled and can be started
+     */
+    public synchronized final boolean isEnabled() {
+        return serviceFactory.isEnabled();
+    }
+
+    /**
+     * Changes the enabled status.
+     *
+     * @param enabled the new enabled flag
+     */
+    public synchronized final void setEnabled(boolean enabled) {
+        serviceFactory.setEnabled(enabled);
+    }
+
+    public synchronized final long getStartTime() {
+        return startTime;
+    }
+
+    public int getState() {
+        return serviceInstanceState.getState();
+    }
+
+
+    /**
+     * Moves this service to the starting state and then attempts to move this service immediately
+     * to the running state.
+     *
+     * @throws IllegalStateException If the service is disabled
+     */
+    public final void start() {
+        synchronized (this) {
+            if (dead) {
+                throw new IllegalStateException("A dead service can not be started: objectName=" + objectName);
+            }
+            if (!isEnabled()) {
+                throw new IllegalStateException("A disabled service can not be started: objectName=" + objectName);
+            }
+        }
+        serviceInstanceState.start();
+    }
+
+    /**
+     * Starts this ServiceInstance and then attempts to start all of its start dependent children.
+     *
+     * @throws IllegalStateException If the service is disabled
+     */
+    public final void startRecursive() {
+        synchronized (this) {
+            if (dead) {
+                throw new IllegalStateException("A dead service can not be started: objectName=" + objectName);
+            }
+            if (!isEnabled()) {
+                throw new IllegalStateException("A disabled service can not be started: objectName=" + objectName);
+            }
+        }
+        serviceInstanceState.startRecursive();
+    }
+
+    /**
+     * Moves this service to the STOPPING state, calls stop on all start dependent children, and then attempt
+     * to move this service to the STOPPED state.
+     */
+    public final void stop() {
+        serviceInstanceState.stop();
+    }
+
+    boolean createInstance() throws Exception {
+        synchronized (this) {
+            // first check we are still in the correct state to start
+            if (instanceState == CREATING || instanceState == RUNNING) {
+                // another thread already completed starting
+                return false;
+            } else if (instanceState == DESTROYING) {
+                // this should never ever happen... this method is protected by the ServiceInstanceState class which should
+                // prevent stuff like this happening, but check anyway
+                throw new IllegalStateException("A stopping instance can not be started until fully stopped");
+            }
+            assert instanceState == DESTROYED;
+
+            // Call all start on every reference.  This way the dependecies are held until we can start
+            boolean allStarted = true;
+            for (Iterator iterator = dependencies.iterator(); iterator.hasNext();) {
+                ServiceDependency dependency = (ServiceDependency) iterator.next();
+                allStarted = dependency.start() && allStarted;
+            }
+            if (!allStarted) {
+                return false;
+            }
+
+            // we are definately going to (try to) start... if this fails the must clean up these variables
+            instanceState = CREATING;
+            startTime = System.currentTimeMillis();
+        }
+
+        Object instance = null;
+        try {
+            instance = serviceFactory.createService(serviceContext);
+
+            // all done... we are now fully running
+            synchronized (this) {
+                target = instance;
+                instanceState = RUNNING;
+                this.notifyAll();
+            }
+
+            return true;
+        } catch (Throwable t) {
+            // something went wrong... we need to destroy this instance
+            synchronized (this) {
+                instanceState = DESTROYING;
+            }
+
+            serviceFactory.destroyService(serviceContext, instance);
+
+            // bean has been notified... drop our reference
+            synchronized (this) {
+                for (Iterator iterator = dependencies.iterator(); iterator.hasNext();) {
+                    ServiceDependency dependency = (ServiceDependency) iterator.next();
+                    dependency.stop();
+                }
+                target = null;
+                instanceState = DESTROYED;
+                startTime = 0;
+                this.notifyAll();
+            }
+
+            if (t instanceof Exception) {
+                throw (Exception) t;
+            } else if (t instanceof Error) {
+                throw (Error) t;
+            } else {
+                throw new Error(t);
+            }
+        }
+    }
+
+    boolean destroyInstance() throws Exception {
+        Object instance;
+        synchronized (this) {
+            // if the instance is being created we need to wait
+            //  for it to finish before we can try to stop it
+            while (instanceState == CREATING) {
+                // todo should we limit this wait?  If so, how do we configure the wait time?
+                try {
+                    this.wait();
+                } catch (InterruptedException e) {
+                    // clear the interrupted flag
+                    Thread.interrupted();
+                    // rethrow the interrupted exception.... someone was sick of us waiting
+                    throw e;
+                }
+            }
+
+            if (instanceState == DESTROYING || instanceState == DESTROYED) {
+                // another thread is already stopping or has already stopped
+                return false;
+            }
+            assert instanceState == RUNNING;
+
+            // we are definately going to stop... if this fails we must clean up these variables
+            instanceState = DESTROYING;
+            instance = target;
+        }
+
+        try {
+            // we notify the bean before removing our dependencies so the dependencies can be called back while stopping
+            serviceFactory.destroyService(serviceContext, instance);
+        } finally {
+            // bean has been notified... drop the dependencies
+            synchronized (this) {
+                for (Iterator iterator = dependencies.iterator(); iterator.hasNext();) {
+                    ServiceDependency dependency = (ServiceDependency) iterator.next();
+                    dependency.stop();
+                }
+                target = null;
+                instanceState = DESTROYED;
+                startTime = 0;
+            }
+        }
+        return true;
+    }
+
+    private static final class ServiceInstanceContext implements ServiceContext {
+        /**
+         * The ServiceInstance which owns the target.
+         */
+        private final ServiceInstance serviceInstance;
+
+        /**
+         * Creates a new context for a target.
+         *
+         * @param serviceInstance the ServiceInstance
+         */
+        public ServiceInstanceContext(ServiceInstance serviceInstance) {
+            this.serviceInstance = serviceInstance;
+        }
+
+        public Kernel getKernel() {
+            return serviceInstance.getKernel();
+        }
+
+        public String getObjectName() {
+            return serviceInstance.getObjectName().getCanonicalName();
+        }
+
+        public ClassLoader getClassLoader() {
+            return serviceInstance.getClassLoader();
+        }
+
+        public int getState() {
+            return serviceInstance.getState();
+        }
+
+        public void stop() throws Exception {
+            synchronized (serviceInstance) {
+                if (serviceInstance.instanceState == CREATING) {
+                    throw new IllegalStateException("Stop can not be called until instance is fully started");
+                } else if (serviceInstance.instanceState == DESTROYING) {
+                    log.debug("Stop ignored.  Service is already being stopped");
+                    return;
+                } else if (serviceInstance.instanceState == DESTROYED) {
+                    log.debug("Stop ignored.  Service is already stopped");
+                    return;
+                }
+            }
+            serviceInstance.stop();
+        }
+    }
+
+    public boolean equals(Object obj) {
+        if (obj == this) return true;
+        if (obj instanceof ServiceInstance == false) return false;
+        return objectName.equals(((ServiceInstance) obj).objectName);
+    }
+
+    public int hashCode() {
+        return objectName.hashCode();
+    }
+
+    public String toString() {
+        return objectName.getCanonicalName();
+    }
+}
diff --git a/kernel/src/java/org/gbean/kernel/runtime/ServiceInstanceState.java b/kernel/src/java/org/gbean/kernel/runtime/ServiceInstanceState.java
new file mode 100644
index 0000000..dcd3bed
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/runtime/ServiceInstanceState.java
@@ -0,0 +1,437 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.kernel.runtime;
+
+import java.util.Iterator;
+import java.util.Set;
+import javax.management.ObjectName;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.gbean.kernel.Kernel;
+import org.gbean.kernel.DependencyManager;
+import org.gbean.kernel.LifecycleListener;
+import org.gbean.kernel.ServiceNotFoundException;
+import org.gbean.kernel.LifecycleAdapter;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public final class ServiceInstanceState {
+    private static final Log log = LogFactory.getLog(ServiceInstanceState.class);
+
+    /**
+     * The ServiceInstance in which this server is registered.
+     */
+    private final ServiceInstance serviceInstance;
+
+    /**
+     * The kernel in which this server is registered.
+     */
+    private final Kernel kernel;
+
+    /**
+     * The unique name of this service.
+     */
+    private final ObjectName objectName;
+
+    /**
+     * The dependency manager
+     */
+    private final DependencyManager dependencyManager;
+
+    /**
+     * The broadcaster of lifecycle events
+     */
+    private final LifecycleBroadcaster lifecycleBroadcaster;
+
+    /**
+     * The listener for the of the object blocking the start of this service.
+     * When the blocker dies we attempt to start.
+     */
+    private LifecycleListener blockerListener;
+
+    // This must be volatile otherwise getState must be synchronized which will result in deadlock as dependent
+    // objects check if each other are in one state or another (i.e., classic A calls B while B calls A)
+    private volatile ServiceState state = ServiceState.STOPPED;
+
+    ServiceInstanceState(ObjectName objectName, Kernel kernel, DependencyManager dependencyManager, ServiceInstance serviceInstance, LifecycleBroadcaster lifecycleBroadcaster) {
+        this.objectName = objectName;
+        this.kernel = kernel;
+        this.dependencyManager = dependencyManager;
+        this.serviceInstance = serviceInstance;
+        this.lifecycleBroadcaster = lifecycleBroadcaster;
+    }
+
+    /**
+     * Moves this service to the starting state and then attempts to move this service immediately
+     * to the running state.
+     * <p/>
+     * Note:  This method cannot be called while the current thread holds a synchronized lock on this service,
+     * because this method sends lifecycle notifications. Sending a general notification from a synchronized block
+     * is a bad idea and therefore not allowed.
+     */
+    public void start() {
+        assert !Thread.holdsLock(this): "This method cannot be called while holding a synchronized lock on this";
+
+        // Move to the starting state
+        ServiceState originalState;
+        synchronized (this) {
+            originalState = getStateInstance();
+            if (originalState == ServiceState.RUNNING) {
+                return;
+            }
+            // only try to change states if we are not already starting
+            if (originalState != ServiceState.STARTING) {
+                setStateInstance(ServiceState.STARTING);
+            }
+        }
+
+        // only fire a notification if we are not already starting
+        if (originalState != ServiceState.STARTING) {
+            lifecycleBroadcaster.fireStartingEvent();
+        }
+
+        attemptFullStart();
+    }
+
+    /**
+     * Starts this service and then attempts to start all of its start dependent children.
+     * <p/>
+     * Note:  This method cannot be call while the current thread holds a synchronized lock on this service,
+     * because this method sends lifecycle notifications.  Sending a general notification from a synchronized block
+     * is a bad idea and therefore not allowed.
+     */
+    public void startRecursive() {
+        assert !Thread.holdsLock(this): "This method cannot be called while holding a synchronized lock on this";
+
+        ServiceState state = getStateInstance();
+        if (state != ServiceState.STOPPED) {
+            // Cannot startRecursive while in the stopping state
+            // Dain: I don't think we can throw an exception here because there is no way for the caller
+            // to lock the instance and check the state before calling
+            return;
+        }
+
+        // get myself starting
+        start();
+
+        // startRecursive all of objects that depend on me
+        Set dependents = dependencyManager.getChildren(objectName);
+        for (Iterator iterator = dependents.iterator(); iterator.hasNext();) {
+            ObjectName dependent = (ObjectName) iterator.next();
+            try {
+                if (kernel.isServiceEnabled(dependent)) {
+                    kernel.startRecursiveService(dependent);
+                }
+            } catch (ServiceNotFoundException e) {
+                // this is ok the service died before we could start it
+                continue;
+            } catch (Exception e) {
+                // the is something wrong with this service... skip it
+                continue;
+            }
+        }
+    }
+
+    /**
+     * Moves this service to the STOPPING state, calls stop on all start dependent children, and then attempt
+     * to move this service to the STOPPED state.
+     * <p/>
+     * Note:  This method can not be call while the current thread holds a syncronized lock on this service,
+     * because this method sends lifecycle notifications.  Sending a general notification from a synchronized block
+     * is a bad idea and therefore not allowed.
+     */
+    public void stop() {
+        assert !Thread.holdsLock(this): "This method cannot be called while holding a synchronized lock on this";
+
+        // move to the stopping state
+        ServiceState originalState;
+        synchronized (this) {
+            originalState = getStateInstance();
+            if (originalState == ServiceState.STOPPED) {
+                return;
+            }
+
+            // only try to change states if we are not already stopping
+            if (originalState != ServiceState.STOPPING) {
+                setStateInstance(ServiceState.STOPPING);
+            }
+        }
+
+        // only fire a notification if we are not already stopping
+        if (originalState != ServiceState.STOPPING) {
+            lifecycleBroadcaster.fireStoppingEvent();
+        }
+
+        // Don't try to stop dependents from within a synchronized block... this should reduce deadlocks
+
+        // stop all of my dependent objects
+        Set dependents = dependencyManager.getChildren(objectName);
+        for (Iterator iterator = dependents.iterator(); iterator.hasNext();) {
+            ObjectName child = (ObjectName) iterator.next();
+            try {
+                log.trace("Checking if child is running: child=" + child);
+                if (kernel.getServiceState(child) == ServiceState.RUNNING_INDEX) {
+                    log.trace("Stopping child: child=" + child);
+                    kernel.stopService(child);
+                    log.trace("Stopped child: child=" + child);
+                }
+            } catch (Exception ignore) {
+                // not a big deal... did my best
+            }
+        }
+
+        attemptFullStop();
+    }
+
+    /**
+     * Attempts to bring the component into running state. If an Exception occurs while
+     * starting the component, the component will be failed.
+     * <p/>
+     * <p/>
+     * Note: Do not call this from within a synchronized block as it makes may send a lifecycle notification
+     */
+    void attemptFullStart() {
+        assert !Thread.holdsLock(this): "This method cannot be called while holding a synchronized lock on this";
+
+        synchronized (this) {
+            // if we are still trying to start and can start now... start
+            if (getStateInstance() != ServiceState.STARTING) {
+                return;
+            }
+
+            if (blockerListener != null) {
+                log.trace("Cannot run because service is still being blocked");
+                return;
+            }
+
+            // check if an service is blocking us from starting
+            final ObjectName blocker = dependencyManager.checkBlocker(objectName);
+            if (blocker != null) {
+                blockerListener = new LifecycleAdapter() {
+
+                    public void stopped(ObjectName objectName) {
+                        checkBlocker(objectName);
+                    }
+
+                    public void unloaded(ObjectName objectName) {
+                        checkBlocker(objectName);
+                    }
+
+                    private void checkBlocker(ObjectName objectName) {
+                        synchronized (ServiceInstanceState.this) {
+                            if (!objectName.equals(blocker)) {
+                                // it did not start so just exit this method
+                                return;
+                            }
+
+                            // it started, so remove the blocker and attempt a full start
+                            kernel.removeLifecycleListener(this);
+                            ServiceInstanceState.this.blockerListener = null;
+                        }
+
+                        try {
+                            attemptFullStart();
+                        } catch (Exception e) {
+                            log.warn("A problem occured while attempting to start", e);
+                        }
+                    }
+                };
+                // register the listener and return
+                kernel.addLifecycleListener(blockerListener, blocker);
+                return;
+            }
+
+            // check if all of the services we depend on are running
+            Set parents = dependencyManager.getParents(objectName);
+            for (Iterator i = parents.iterator(); i.hasNext();) {
+                ObjectName parent = (ObjectName) i.next();
+                if (!kernel.isLoaded(parent)) {
+                    log.trace("Cannot run because parent is not registered: parent=" + parent);
+                    return;
+                }
+                try {
+                    log.trace("Checking if parent is running: parent=" + parent);
+                    if (kernel.getServiceState(parent) != ServiceState.RUNNING_INDEX) {
+                        log.trace("Cannot run because parent is not running: parent=" + parent);
+                        return;
+                    }
+                    log.trace("Parent is running: parent=" + parent);
+                } catch (ServiceNotFoundException e) {
+                    // depended on instance was removed bewteen the register check and the invoke
+                    log.trace("Cannot run because parent is not registered: parent=" + parent);
+                    return;
+                } catch (Exception e) {
+                    // problem getting the attribute, parent has most likely failed
+                    log.trace("Cannot run because an error occurred while checking if parent is running: parent=" + parent);
+                    return;
+                }
+            }
+        }
+
+        try {
+            // try to create the instance
+            if (!serviceInstance.createInstance()) {
+                // instance is not ready to start... this is normally caused by references
+                // not being available, but could be because someone alreayd started the service.
+                // in another thread.  The reference will log a debug message about why
+                // it could not start
+                return;
+            }
+        } catch (Throwable t) {
+            // oops there was a problem, stop the service
+            setStateInstance(ServiceState.STOPPING);
+            lifecycleBroadcaster.fireStoppingEvent();
+            setStateInstance(ServiceState.STOPPED);
+            lifecycleBroadcaster.fireStoppedEvent();
+
+            log.error("Error while starting; Service is now in the STOPPED state: objectName=\"" + objectName + "\"", t);
+            return;
+        }
+
+        // started successfully... notify everyone else
+        setStateInstance(ServiceState.RUNNING);
+        lifecycleBroadcaster.fireRunningEvent();
+    }
+
+    /**
+     * Attempt to bring the component into the fully stopped state.
+     * If an exception occurs while stopping the component, the component will be failed.
+     * <p/>
+     * <p/>
+     * Note: Do not call this from within a synchronized block as it may send a lifecycle notification
+     */
+    void attemptFullStop() {
+        assert !Thread.holdsLock(this): "This method cannot be called while holding a synchronized lock on this";
+
+        // check if we are able to stop
+        synchronized (this) {
+            // if we are still trying to stop...
+            if (getStateInstance() != ServiceState.STOPPING) {
+                return;
+            }
+
+            // check if all of the service depending on us are stopped
+            Set children = dependencyManager.getChildren(objectName);
+            for (Iterator i = children.iterator(); i.hasNext();) {
+                ObjectName child = (ObjectName) i.next();
+                if (kernel.isLoaded(child)) {
+                    try {
+                        log.trace("Checking if child is stopped: child=" + child);
+                        int state = kernel.getServiceState(child);
+                        if (state == ServiceState.RUNNING_INDEX) {
+                            log.trace("Cannot stop because child is still running: child=" + child);
+                            return;
+                        }
+                    } catch (ServiceNotFoundException e) {
+                        // depended on instance was removed between the register check and the invoke
+                    } catch (Exception e) {
+                        // problem getting the attribute, depended on bean has most likely failed
+                        log.trace("Cannot run because an error occurred while checking if child is stopped: child=" + child);
+                        return;
+                    }
+                }
+            }
+        }
+
+        // all is clear to stop... try to stop
+        try {
+            if (!serviceInstance.destroyInstance()) {
+                // instance is not ready to stop... this is because another thread has
+                // already stopped the service.
+                return;
+            }
+        } catch (Throwable t) {
+            log.error("Error while stopping; Service is now in the STOPPED state: objectName=\"" + objectName + "\"", t);
+        } finally {
+            // we are always stopped at this point
+            setStateInstance(ServiceState.STOPPED);
+            lifecycleBroadcaster.fireStoppedEvent();
+        }
+    }
+
+    public int getState() {
+        return state.getIndex();
+    }
+
+    public ServiceState getStateInstance() {
+        return state;
+    }
+
+    /**
+     * Set the Component state.
+     *
+     * @param newState the target state to transition
+     * @throws IllegalStateException Thrown if the transition is not supported by the lifecycle contract.
+     */
+    private synchronized void setStateInstance(ServiceState newState) throws IllegalStateException {
+        switch (state.getIndex()) {
+            case ServiceState.STOPPED_INDEX:
+                switch (newState.getIndex()) {
+                    case ServiceState.STARTING_INDEX:
+                        break;
+                    case ServiceState.STOPPED_INDEX:
+                    case ServiceState.RUNNING_INDEX:
+                    case ServiceState.STOPPING_INDEX:
+                        throw new IllegalStateException("Cannot transition to " + newState + " state from " + state);
+                }
+                break;
+
+            case ServiceState.STARTING_INDEX:
+                switch (newState.getIndex()) {
+                    case ServiceState.RUNNING_INDEX:
+                    case ServiceState.STOPPING_INDEX:
+                        break;
+                    case ServiceState.STOPPED_INDEX:
+                    case ServiceState.STARTING_INDEX:
+                        throw new IllegalStateException("Cannot transition to " + newState + " state from " + state);
+                }
+                break;
+
+            case ServiceState.RUNNING_INDEX:
+                switch (newState.getIndex()) {
+                    case ServiceState.STOPPING_INDEX:
+                        break;
+                    case ServiceState.STOPPED_INDEX:
+                    case ServiceState.STARTING_INDEX:
+                    case ServiceState.RUNNING_INDEX:
+                        throw new IllegalStateException("Cannot transition to " + newState + " state from " + state);
+                }
+                break;
+
+            case ServiceState.STOPPING_INDEX:
+                switch (newState.getIndex()) {
+                    case ServiceState.STOPPED_INDEX:
+                        break;
+                    case ServiceState.STARTING_INDEX:
+                    case ServiceState.RUNNING_INDEX:
+                    case ServiceState.STOPPING_INDEX:
+                        throw new IllegalStateException("Cannot transition to " + newState + " state from " + state);
+                }
+                break;
+
+        }
+        log.debug(toString() + " State changed from " + state + " to " + newState);
+        state = newState;
+    }
+
+    public String toString() {
+        return "ServiceInstanceState for: " + objectName;
+    }
+
+}
diff --git a/kernel/src/java/org/gbean/kernel/runtime/ServiceState.java b/kernel/src/java/org/gbean/kernel/runtime/ServiceState.java
new file mode 100644
index 0000000..ff04f06
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/runtime/ServiceState.java
@@ -0,0 +1,91 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.gbean.kernel.runtime;
+
+import java.io.Serializable;
+
+/**
+ * @version $Rev: 54804 $ $Date: 2004-10-14 14:09:29 -0700 (Thu, 14 Oct 2004) $
+ */
+public final class ServiceState implements Serializable {
+    public static final int STARTING_INDEX = 0;
+    public static final int RUNNING_INDEX = 1;
+    public static final int STOPPING_INDEX = 2;
+    public static final int STOPPED_INDEX = 3;
+
+    public static final ServiceState STARTING = new ServiceState("starting", STARTING_INDEX);
+    public static final ServiceState RUNNING = new ServiceState("running", RUNNING_INDEX);
+    public static final ServiceState STOPPING = new ServiceState("stopping", STOPPING_INDEX);
+    public static final ServiceState STOPPED = new ServiceState("stopped", STOPPED_INDEX);
+
+    private static final ServiceState[] fromInt = {STARTING, RUNNING, STOPPING, STOPPED};
+
+    /**
+     * Get a State from an int index
+     *
+     * @param index int index of the state
+     * @return The State instance or null if no such State.
+     */
+    public static ServiceState fromIndex(int index) {
+        if (index < 0 || index >= fromInt.length) {
+            return null;
+        }
+        return fromInt[index];
+    }
+
+    public static String toString(int state) {
+        if (state < 0 || state >= fromInt.length) {
+            throw new IllegalArgumentException("State must be between 0 and " + fromInt.length);
+        }
+        return fromInt[state].name;
+    }
+
+    /**
+     * The user readable name of this state.
+     */
+    private final String name;
+
+    /**
+     * The index of this state.
+     */
+    private final int index;
+
+    private ServiceState(String name, int index) {
+        this.name = name;
+        this.index = index;
+    }
+
+    /**
+     * Gets the integer index value of this state
+     */
+    public int getIndex() {
+        return index;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String toString() {
+        return name;
+    }
+
+    private Object readResolve() {
+        return fromInt[index];
+    }
+}
diff --git a/kernel/src/java/org/gbean/kernel/simple/SimpleDependencyManager.java b/kernel/src/java/org/gbean/kernel/simple/SimpleDependencyManager.java
new file mode 100644
index 0000000..32f9a91
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/simple/SimpleDependencyManager.java
@@ -0,0 +1,278 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.gbean.kernel.simple;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.management.ObjectName;
+
+import org.gbean.kernel.Kernel;
+import org.gbean.kernel.DependencyManager;
+import org.gbean.kernel.LifecycleListener;
+import org.gbean.kernel.LifecycleAdapter;
+import org.gbean.kernel.ServiceName;
+
+/**
+ * DependencyManager is the record keeper of the dependencies in the kernel.  The DependencyManager
+ * does not enforce any dependencies, it is simply a place where components can register their intent
+ * to be dependent on another component.  Since a service can pretty much do whatever it wants
+ * a service must watch the services it depends on to assure that they are following the
+ * lifecycle contract.
+ * <p/>
+ * The DependencyManager uses the nomenclature of parent-child where a child is dependent on a parent.
+ * The names parent and child have no other meaning are just a convience to make the code readable.
+ *
+ * @version $Rev: 124822 $ $Date: 2005-01-10 11:01:13 -0800 (Mon, 10 Jan 2005) $
+ */
+public class SimpleDependencyManager implements DependencyManager {
+    /**
+     * The lifecycleMonitor informs us when services go off line,
+     * so we can clean up the lingering dependencies.
+     */
+    private final Kernel kernel;
+
+    /**
+     * Listenes for services to unregister and removes all dependencies associated with the dependency
+     */
+    private final LifecycleListener lifecycleListener = new DependencyManagerLifecycleListener();
+
+    /**
+     * A map from child names to a list of parents.
+     */
+    private final Map childToParentMap = new HashMap();
+
+    /**
+     * A map from parent back to a list of its children.
+     */
+    private final Map parentToChildMap = new HashMap();
+
+    /**
+     * A map from a component's ObjectName to the list of ObjectPatterns that the component is blocking
+     * from starting.
+     */
+    private final Map startHoldsMap = new HashMap();
+
+    private static final ObjectName ALL = ServiceName.createName("*:*");
+
+    public SimpleDependencyManager(Kernel kernel) {
+        assert kernel != null;
+        this.kernel = kernel;
+    }
+
+    // todo throw illegal state from all methods when not started
+    public synchronized void start() {
+        this.kernel.addLifecycleListener(lifecycleListener, ALL);
+    }
+    public synchronized void stop() {
+        kernel.removeLifecycleListener(lifecycleListener);
+        childToParentMap.clear();
+        parentToChildMap.clear();
+        startHoldsMap.clear();
+    }
+
+    /**
+     * Declares a dependency from a child to a parent.
+     *
+     * @param child the dependent component
+     * @param parent the component the child is depending on
+     */
+    public synchronized void addDependency(ObjectName child, ObjectName parent) {
+        Set parents = (Set) childToParentMap.get(child);
+        if (parents == null) {
+            parents = new HashSet();
+            childToParentMap.put(child, parents);
+        }
+        parents.add(parent);
+
+        Set children = (Set) parentToChildMap.get(parent);
+        if (children == null) {
+            children = new HashSet();
+            parentToChildMap.put(parent, children);
+        }
+        children.add(child);
+    }
+
+    /**
+     * Removes a dependency from a child to a parent
+     *
+     * @param child the dependnet component
+     * @param parent the component that the child wil no longer depend on
+     */
+    public synchronized void removeDependency(ObjectName child, ObjectName parent) {
+        Set parents = (Set) childToParentMap.get(child);
+        if (parents != null) {
+            parents.remove(parent);
+        }
+
+        Set children = (Set) parentToChildMap.get(parent);
+        if (children != null) {
+            children.remove(child);
+        }
+    }
+
+    /**
+     * Removes all dependencies for a child
+     *
+     * @param child the component that will no longer depend on anything
+     */
+    public synchronized void removeAllDependencies(ObjectName child) {
+        Set parents = (Set) childToParentMap.remove(child);
+        if (parents == null) {
+            return;
+        }
+        for (Iterator iterator = parents.iterator(); iterator.hasNext();) {
+            ObjectName parent = (ObjectName) iterator.next();
+            Set children = (Set) parentToChildMap.get(parent);
+            if (children != null) {
+                children.remove(child);
+            }
+
+        }
+    }
+
+    /**
+     * Adds dependencies from the child to every parent in the parents set
+     *
+     * @param child the dependent component
+     * @param parents the set of components the child is depending on
+     */
+    public synchronized void addDependencies(ObjectName child, Set parents) {
+        Set existingParents = (Set) childToParentMap.get(child);
+        if (existingParents == null) {
+            existingParents = new HashSet(parents);
+            childToParentMap.put(child, existingParents);
+        } else {
+            existingParents.addAll(parents);
+        }
+
+        for (Iterator i = parents.iterator(); i.hasNext();) {
+            Object startParent = i.next();
+            Set children = (Set) parentToChildMap.get(startParent);
+            if (children == null) {
+                children = new HashSet();
+                parentToChildMap.put(startParent, children);
+            }
+            children.add(child);
+        }
+    }
+
+    /**
+     * Gets the set of parents that the child is depending on
+     *
+     * @param child the dependent component
+     * @return a collection containing all of the components the child depends on; will never be null
+     */
+    public synchronized Set getParents(ObjectName child) {
+        Set parents = (Set) childToParentMap.get(child);
+        if (parents == null) {
+            return Collections.EMPTY_SET;
+        }
+        return new HashSet(parents);
+    }
+
+    /**
+     * Gets all of the services that have a dependency on the specified startParent.
+     *
+     * @param parent the component the returned childen set depend on
+     * @return a collection containing all of the components that depend on the parent; will never be null
+     */
+    public synchronized Set getChildren(ObjectName parent) {
+        Set children = (Set) parentToChildMap.get(parent);
+        if (children == null) {
+            return Collections.EMPTY_SET;
+        }
+        return new HashSet(children);
+    }
+
+    /**
+     * Adds a hold on a collection of object name patterns.  If the name of a component matches an object name
+     * pattern in the collection, the component should not start.
+     *
+     * @param objectName the name of the component placing the holds
+     * @param holds a collection of object name patterns which should not start
+     */
+    public synchronized void addStartHolds(ObjectName objectName, Collection holds) {
+        Collection currentHolds = (Collection) startHoldsMap.get(objectName);
+        if (currentHolds == null) {
+            currentHolds = new LinkedList(holds);
+            startHoldsMap.put(objectName, currentHolds);
+        } else {
+            currentHolds.addAll(holds);
+        }
+    }
+
+    /**
+     * Removes a collection of holds.
+     *
+     * @param objectName the object name of the components owning the holds
+     * @param holds a collection of the holds to remove
+     */
+    public synchronized void removeStartHolds(ObjectName objectName, Collection holds) {
+        Collection currentHolds = (Collection) startHoldsMap.get(objectName);
+        if (currentHolds != null) {
+            currentHolds.removeAll(holds);
+        }
+    }
+
+    /**
+     * Removes all of the holds owned by a component.
+     *
+     * @param objectName the object name of the component that will no longer have any holds
+     */
+    public synchronized void removeAllStartHolds(ObjectName objectName) {
+        startHoldsMap.remove(objectName);
+    }
+
+    /**
+     * Gets the object name of the service blocking the start specified service.
+     *
+     * @param objectName the service to check for blockers
+     * @return the service blocking the specified service, or null if there are no blockers
+     */
+    public synchronized ObjectName checkBlocker(ObjectName objectName) {
+        // check if objectName name is on one of the hold lists
+        for (Iterator iterator = startHoldsMap.keySet().iterator(); iterator.hasNext();) {
+            ObjectName blocker = (ObjectName) iterator.next();
+            List holds = (List) startHoldsMap.get(blocker);
+            for (Iterator holdsIterator = holds.iterator(); holdsIterator.hasNext();) {
+                ObjectName pattern = (ObjectName) holdsIterator.next();
+                if (pattern.apply(objectName)) {
+                    return blocker;
+                }
+            }
+        }
+        return null;
+    }
+
+    private class DependencyManagerLifecycleListener extends LifecycleAdapter {
+        public void unloaded(ObjectName objectName) {
+            synchronized (SimpleDependencyManager.this) {
+                removeAllDependencies(objectName);
+                removeAllStartHolds(objectName);
+            }
+
+        }
+    }
+}
diff --git a/kernel/src/java/org/gbean/kernel/simple/SimpleKernel.java b/kernel/src/java/org/gbean/kernel/simple/SimpleKernel.java
new file mode 100644
index 0000000..f390560
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/simple/SimpleKernel.java
@@ -0,0 +1,328 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.gbean.kernel.simple;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Set;
+import javax.management.ObjectName;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.gbean.kernel.DependencyManager;
+import org.gbean.kernel.Kernel;
+import org.gbean.kernel.KernelRegistry;
+import org.gbean.kernel.LifecycleListener;
+import org.gbean.kernel.ServiceAlreadyExistsException;
+import org.gbean.kernel.ServiceNotFoundException;
+import org.gbean.kernel.runtime.ServiceInstance;
+import org.gbean.service.AbstractServiceFactory;
+import org.gbean.service.ServiceContext;
+import org.gbean.service.ServiceFactory;
+
+
+/**
+ * @version $Rev: 154947 $ $Date: 2005-02-22 20:10:45 -0800 (Tue, 22 Feb 2005) $
+ */
+public class SimpleKernel implements Kernel {
+    /**
+     * Name of this kernel
+     */
+    private final String kernelName;
+
+    /**
+     * The log
+     */
+    private Log log;
+
+    /**
+     * Is this kernel running?
+     */
+    private boolean running;
+
+    /**
+     * The timestamp when the kernel was started
+     */
+    private Date bootTime;
+
+    /**
+     * The simple registry
+     */
+    private final SimpleRegistry registry;
+
+    /**
+     * Listeners for when the kernel shutdown
+     */
+    private final LinkedList shutdownHooks = new LinkedList();
+
+    /**
+     * This manager is used by the kernel to manage dependencies between services
+     */
+    private final DependencyManager dependencyManager;
+
+    /**
+     * Monitors the lifecycle of all services.
+     */
+    private final SimpleLifecycleMonitor lifecycleMonitor;
+
+    /**
+     * Construct a Kernel with the specified name.
+     *
+     * @param kernelName the name of the kernel
+     */
+    public SimpleKernel(String kernelName) {
+        if (kernelName.indexOf(':') >= 0 || kernelName.indexOf('*') >= 0 || kernelName.indexOf('?') >= 0) {
+            throw new IllegalArgumentException("Kernel name may not contain a ':', '*' or '?' character");
+        }
+        this.kernelName = kernelName;
+        lifecycleMonitor = new SimpleLifecycleMonitor();
+        this.registry = new SimpleRegistry(kernelName);
+        dependencyManager = new SimpleDependencyManager(this);
+    }
+
+    public String getKernelName() {
+        return kernelName;
+    }
+
+    /**
+     * @deprecated Do not use.  This is only here for the geronimo bridge and will go away as soon as possible.
+     */
+    public DependencyManager getDependencyManager() {
+        return dependencyManager;
+    }
+
+    public boolean isLoaded(ObjectName name) {
+        return registry.isRegistered(name);
+    }
+
+    public void loadService(ObjectName objectName, ServiceFactory serviceFactory, ClassLoader classLoader) throws ServiceAlreadyExistsException {
+        ServiceInstance serviceInstance = new ServiceInstance(objectName,
+                serviceFactory,
+                this,
+                dependencyManager,
+                lifecycleMonitor.createLifecycleBroadcaster(objectName),
+                classLoader);
+        registry.register(objectName, serviceInstance);
+        serviceInstance.init();
+    }
+
+    public void startService(ObjectName name) throws ServiceNotFoundException, IllegalStateException {
+        ServiceInstance serviceInstance = registry.getServiceInstance(name);
+        serviceInstance.start();
+    }
+
+    public void startRecursiveService(ObjectName name) throws ServiceNotFoundException, IllegalStateException {
+        ServiceInstance serviceInstance = registry.getServiceInstance(name);
+        serviceInstance.startRecursive();
+    }
+
+    public void stopService(ObjectName name) throws ServiceNotFoundException, IllegalStateException {
+        ServiceInstance serviceInstance = registry.getServiceInstance(name);
+        serviceInstance.stop();
+    }
+
+    public void unloadService(ObjectName name) throws ServiceNotFoundException, IllegalStateException {
+        ServiceInstance serviceInstance = registry.getServiceInstance(name);
+        serviceInstance.destroy();
+        registry.unregister(name);
+    }
+
+    public Object getService(ObjectName name) throws ServiceNotFoundException, IllegalStateException {
+        ServiceInstance serviceInstance = registry.getServiceInstance(name);
+        Object instance = serviceInstance.getInstance();
+        if (instance == null) {
+            throw new IllegalStateException("Service is not running: " + name);
+        }
+        return instance;
+    }
+
+    public ServiceFactory getServiceFactory(ObjectName name) throws ServiceNotFoundException {
+        ServiceInstance serviceInstance = registry.getServiceInstance(name);
+        return serviceInstance.getServiceFactory();
+    }
+
+    public int getServiceState(ObjectName name) throws ServiceNotFoundException {
+        ServiceInstance serviceInstance = registry.getServiceInstance(name);
+        return serviceInstance.getState();
+    }
+
+    public long getServiceStartTime(ObjectName name) throws ServiceNotFoundException {
+        ServiceInstance serviceInstance = registry.getServiceInstance(name);
+        return serviceInstance.getStartTime();
+    }
+
+    public boolean isServiceEnabled(ObjectName name) throws ServiceNotFoundException {
+        ServiceInstance serviceInstance = registry.getServiceInstance(name);
+        return serviceInstance.isEnabled();
+    }
+
+    public void setServiceEnabled(ObjectName name, boolean enabled) throws ServiceNotFoundException {
+        ServiceInstance serviceInstance = registry.getServiceInstance(name);
+        serviceInstance.setEnabled(enabled);
+    }
+
+    public Collection listServices(ObjectName pattern) {
+        Set serviceInstances = registry.listServiceInstances(pattern);
+        ArrayList services = new ArrayList(serviceInstances.size());
+        for (Iterator iterator = serviceInstances.iterator(); iterator.hasNext();) {
+            ServiceInstance serviceInstance = (ServiceInstance) iterator.next();
+            Object service = serviceInstance.getInstance();
+            if (service != null) {
+                services.add(service);
+            }
+        }
+        return services;
+    }
+
+    public Set listServices(Set patterns) {
+        Set services = new HashSet();
+        for (Iterator iterator = patterns.iterator(); iterator.hasNext();) {
+            ObjectName pattern = (ObjectName) iterator.next();
+            services.addAll(listServices(pattern));
+        }
+        return services;
+    }
+
+    public Set listServiceNames(ObjectName pattern) {
+        return registry.listServiceNames(pattern);
+    }
+
+    public Set listServiceNames(Set patterns) {
+        Set services = new HashSet();
+        for (Iterator iterator = patterns.iterator(); iterator.hasNext();) {
+            ObjectName pattern = (ObjectName) iterator.next();
+            services.addAll(listServiceNames(pattern));
+        }
+        return services;
+    }
+
+    public void addLifecycleListener(LifecycleListener lifecycleListener, ObjectName pattern) {
+        addLifecycleListener(lifecycleListener, Collections.singleton(pattern));
+    }
+
+    public void addLifecycleListener(LifecycleListener lifecycleListener, Set patterns) {
+        lifecycleMonitor.addLifecycleListener(lifecycleListener, patterns);
+    }
+
+    public void removeLifecycleListener(LifecycleListener lifecycleListener) {
+        lifecycleMonitor.removeLifecycleListener(lifecycleListener);
+    }
+
+    /**
+     * Boot this Kernel
+     *
+     * @throws Exception if the boot fails
+     */
+    public void boot() throws Exception {
+        if (running) {
+            return;
+        }
+        bootTime = new Date();
+        log = LogFactory.getLog(SimpleKernel.class.getName());
+        log.info("Starting boot");
+
+        lifecycleMonitor.start();
+        registry.start();
+        dependencyManager.start();
+
+        // mount the kernel into the kernel itself so it can be accessed just like
+        // any other service in the system
+        ServiceFactory kernelServiceFactory = new AbstractServiceFactory() {
+            public Object createService(ServiceContext serviceContext) throws Exception {
+                return SimpleKernel.this;
+            }
+        };
+        loadService(KERNEL, kernelServiceFactory, getClass().getClassLoader());
+        startService(KERNEL);
+
+        running = true;
+        log.info("Booted");
+
+        KernelRegistry.registerKernel(this);
+    }
+
+    public Date getBootTime() {
+        return bootTime;
+    }
+
+    public void registerShutdownHook(Runnable hook) {
+        assert hook != null : "Shutdown hook was null";
+        synchronized (shutdownHooks) {
+            shutdownHooks.add(hook);
+        }
+    }
+
+    public void unregisterShutdownHook(Runnable hook) {
+        synchronized (shutdownHooks) {
+            shutdownHooks.remove(hook);
+        }
+    }
+
+    /**
+     * Shutdown this kernel
+     */
+    public void shutdown() {
+        if (!running) {
+            return;
+        }
+        running = false;
+        log.info("Starting kernel shutdown");
+
+        notifyShutdownHooks();
+
+        dependencyManager.stop();
+        registry.stop();
+        lifecycleMonitor.stop();
+
+        synchronized (this) {
+            notify();
+        }
+
+        KernelRegistry.unregisterKernel(this);
+
+        log.info("Kernel shutdown complete");
+    }
+
+    private void notifyShutdownHooks() {
+        while (!shutdownHooks.isEmpty()) {
+            Runnable hook;
+            synchronized (shutdownHooks) {
+                hook = (Runnable) shutdownHooks.removeLast();
+            }
+            try {
+                hook.run();
+            } catch (Throwable e) {
+                log.warn("Error from kernel shutdown hook", e);
+            }
+        }
+    }
+
+    public boolean isRunning() {
+        return running;
+    }
+
+    public ClassLoader getClassLoaderFor(ObjectName name) throws ServiceNotFoundException {
+        ServiceInstance serviceInstance = registry.getServiceInstance(name);
+        return serviceInstance.getClassLoader();
+    }
+}
diff --git a/kernel/src/java/org/gbean/kernel/simple/SimpleKernelFactory.java b/kernel/src/java/org/gbean/kernel/simple/SimpleKernelFactory.java
new file mode 100644
index 0000000..e0d127d
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/simple/SimpleKernelFactory.java
@@ -0,0 +1,29 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.kernel.simple;
+
+import org.gbean.kernel.KernelFactory;
+import org.gbean.kernel.Kernel;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class SimpleKernelFactory extends KernelFactory {
+    public Kernel createKernel(String kernelName) {
+        return new SimpleKernel(kernelName);
+    }
+}
diff --git a/kernel/src/java/org/gbean/kernel/simple/SimpleLifecycle.java b/kernel/src/java/org/gbean/kernel/simple/SimpleLifecycle.java
new file mode 100644
index 0000000..cbd0b63
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/simple/SimpleLifecycle.java
@@ -0,0 +1,26 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.kernel.simple;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public interface SimpleLifecycle {
+    void start() throws Exception;
+
+    void stop();
+}
diff --git a/kernel/src/java/org/gbean/kernel/simple/SimpleLifecycleMonitor.java b/kernel/src/java/org/gbean/kernel/simple/SimpleLifecycleMonitor.java
new file mode 100644
index 0000000..8f12ad2
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/simple/SimpleLifecycleMonitor.java
@@ -0,0 +1,241 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.gbean.kernel.simple;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Set;
+import javax.management.ObjectName;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.gbean.kernel.LifecycleListener;
+import org.gbean.kernel.runtime.LifecycleBroadcaster;
+
+/**
+ * @version $Rev: 71492 $ $Date: 2004-11-14 21:31:50 -0800 (Sun, 14 Nov 2004) $
+ */
+public class SimpleLifecycleMonitor {
+    private static final Log log = LogFactory.getLog(SimpleLifecycleMonitor.class);
+
+    private final Map boundListeners = new LinkedHashMap();
+    private final Map listenerPatterns = new LinkedHashMap();
+
+    public void start() {
+    }
+
+    /**
+     * Frees all resources associated with the monitor.  No futher events will be processed.
+     */
+    public synchronized void stop() {
+        boundListeners.clear();
+        listenerPatterns.clear();
+    }
+
+    /**
+     * Create a lifecycle broadcaster for the specified bean.  This is typically given to the
+     * instance manager to broadcast lifecycle changes
+     * @param objectName the name of the object for which a broadcaster should be created
+     * @return the lifecycle broadcaster
+     */
+    public LifecycleBroadcaster createLifecycleBroadcaster(ObjectName objectName) {
+        return new SimpleLifecycleBroadcaster(objectName);
+    }
+
+    /**
+     * Registers a listener to revieve life cycle events for a set of object name patterns.
+     * @param listener the listener that will receive life cycle events
+     * @param patterns a set of ObjectName patterns
+     */
+    public synchronized void addLifecycleListener(LifecycleListener listener, Set patterns) {
+        for (Iterator patternIterator = patterns.iterator(); patternIterator.hasNext();) {
+            ObjectName pattern = (ObjectName) patternIterator.next();
+            for (Iterator iterator = boundListeners.entrySet().iterator(); iterator.hasNext();) {
+                Map.Entry entry = (Map.Entry) iterator.next();
+                ObjectName source = (ObjectName) entry.getKey();
+                if (pattern.apply(source)) {
+                    List listeners = (List) entry.getValue();
+                    listeners.add(listener);
+                }
+            }
+        }
+        listenerPatterns.put(listener, patterns);
+    }
+
+    /**
+     * Removes the listener from all notifications.
+     * @param listener the listener to unregister
+     */
+    public synchronized void removeLifecycleListener(LifecycleListener listener) {
+        for (Iterator iterator = boundListeners.values().iterator(); iterator.hasNext();) {
+            List listeners = (List) iterator.next();
+            listeners.remove(listener);
+        }
+        listenerPatterns.remove(listener);
+    }
+
+    private synchronized void addSource(ObjectName source) {
+        if (boundListeners.containsKey(source)) {
+            // alreayd registered
+            return;
+        }
+
+        // find all listeners interested in events from this source
+        List listeners = new LinkedList();
+        for (Iterator listenerIterator = listenerPatterns.entrySet().iterator(); listenerIterator.hasNext();) {
+            Map.Entry entry = (Map.Entry) listenerIterator.next();
+            Set patterns = (Set) entry.getValue();
+            for (Iterator patternIterator = patterns.iterator(); patternIterator.hasNext();) {
+                ObjectName pattern = (ObjectName) patternIterator.next();
+                if (pattern.apply(source)) {
+                    LifecycleListener listener = (LifecycleListener) entry.getKey();
+                    listeners.add(listener);
+                }
+            }
+        }
+
+        boundListeners.put(source, listeners);
+    }
+
+    private synchronized void removeSource(ObjectName source) {
+        boundListeners.remove(source);
+    }
+
+    private synchronized List getTargets(ObjectName source) {
+        List targets = (List) boundListeners.get(source);
+        if (targets == null) {
+            // no one is interested in this event
+            return Collections.EMPTY_LIST;
+        } else {
+            return new LinkedList(targets);
+        }
+    }
+
+    private void fireLoadedEvent(ObjectName objectName) {
+        List targets = getTargets(objectName);
+        for (Iterator iterator = targets.iterator(); iterator.hasNext();) {
+            LifecycleListener listener = (LifecycleListener) iterator.next();
+            try {
+                listener.loaded(objectName);
+            } catch (Throwable e) {
+                log.warn("Exception occured while notifying listener", e);
+            }
+        }
+    }
+
+    private void fireStartingEvent(ObjectName source) {
+        List targets = getTargets(source);
+        for (Iterator iterator = targets.iterator(); iterator.hasNext();) {
+            LifecycleListener listener = (LifecycleListener) iterator.next();
+            try {
+                listener.starting(source);
+            } catch (Throwable e) {
+                log.warn("Exception occured while notifying listener", e);
+            }
+        }
+    }
+
+    private void fireRunningEvent(ObjectName source) {
+        List targets = getTargets(source);
+        for (Iterator iterator = targets.iterator(); iterator.hasNext();) {
+            LifecycleListener listener = (LifecycleListener) iterator.next();
+            try {
+                listener.running(source);
+            } catch (Throwable e) {
+                log.warn("Exception occured while notifying listener", e);
+            }
+        }
+    }
+
+    private void fireStoppingEvent(ObjectName source) {
+        List targets = getTargets(source);
+        Collections.reverse(targets);
+        for (Iterator iterator = targets.iterator(); iterator.hasNext();) {
+            LifecycleListener listener = (LifecycleListener) iterator.next();
+            try {
+                listener.stopping(source);
+            } catch (Throwable e) {
+                log.warn("Exception occured while notifying listener", e);
+            }
+        }
+    }
+
+    private void fireStoppedEvent(ObjectName source) {
+        List targets = getTargets(source);
+        Collections.reverse(targets);
+        for (Iterator iterator = targets.iterator(); iterator.hasNext();) {
+            LifecycleListener listener = (LifecycleListener) iterator.next();
+            try {
+                listener.stopped(source);
+            } catch (Throwable e) {
+                log.warn("Exception occured while notifying listener", e);
+            }
+        }
+    }
+
+    private void fireUnloadedEvent(ObjectName source) {
+        List targets = getTargets(source);
+        Collections.reverse(targets);
+        for (Iterator iterator = targets.iterator(); iterator.hasNext();) {
+            LifecycleListener listener = (LifecycleListener) iterator.next();
+            try {
+                listener.unloaded(source);
+            } catch (Throwable e) {
+                log.warn("Exception occured while notifying listener", e);
+            }
+        }
+    }
+
+    private class SimpleLifecycleBroadcaster implements LifecycleBroadcaster {
+        private final ObjectName objectName;
+
+        public SimpleLifecycleBroadcaster(ObjectName objectName) {
+            this.objectName = objectName;
+        }
+
+        public void fireLoadedEvent() {
+            addSource(objectName);
+            SimpleLifecycleMonitor.this.fireLoadedEvent(objectName);
+        }
+
+        public void fireStartingEvent() {
+            SimpleLifecycleMonitor.this.fireStartingEvent(objectName);
+        }
+
+        public void fireRunningEvent() {
+            SimpleLifecycleMonitor.this.fireRunningEvent(objectName);
+        }
+
+        public void fireStoppingEvent() {
+            SimpleLifecycleMonitor.this.fireStoppingEvent(objectName);
+        }
+
+        public void fireStoppedEvent() {
+            SimpleLifecycleMonitor.this.fireStoppedEvent(objectName);
+        }
+
+        public void fireUnloadedEvent() {
+            SimpleLifecycleMonitor.this.fireUnloadedEvent(objectName);
+            removeSource(objectName);
+        }
+    }
+}
diff --git a/kernel/src/java/org/gbean/kernel/simple/SimpleRegistry.java b/kernel/src/java/org/gbean/kernel/simple/SimpleRegistry.java
new file mode 100644
index 0000000..5b155b1
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/simple/SimpleRegistry.java
@@ -0,0 +1,249 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.kernel.simple;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Collections;
+import java.util.regex.Pattern;
+
+import javax.management.ObjectName;
+
+import org.gbean.kernel.runtime.ServiceInstance;
+import org.gbean.kernel.ServiceAlreadyExistsException;
+import org.gbean.kernel.ServiceNotFoundException;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class SimpleRegistry {
+    private final Map registry = new HashMap();
+    private final Map domainIndex = new HashMap();
+    private String defaultDomainName;
+
+    public SimpleRegistry(String defaultDomainName) {
+        this.defaultDomainName = defaultDomainName;
+    }
+
+    public void start() {
+    }
+
+    public void stop() {
+        synchronized (this) {
+            registry.clear();
+            domainIndex.clear();
+        }
+    }
+
+    public boolean isRegistered(ObjectName name) {
+        synchronized (this) {
+            return registry.containsKey(name);
+        }
+    }
+
+    public void register(ObjectName objectName, ServiceInstance serviceInstance) throws ServiceAlreadyExistsException {
+        // do as much work as possible outside of the synchronized block
+        ObjectName name = serviceInstance.getObjectName();
+        String domainName = name.getDomain();
+        if (domainName.length() == 0) {
+            domainName = defaultDomainName;
+        }
+
+        // convert properties list to a HashMap as it is more efficient then the synchronized Hashtable
+        Map properties = new HashMap(name.getKeyPropertyList());
+
+        synchronized (this) {
+            registry.put(name, serviceInstance);
+
+            Map nameToProperties = (Map) domainIndex.get(domainName);
+            if (nameToProperties == null) {
+                nameToProperties = new HashMap();
+                domainIndex.put(domainName, nameToProperties);
+            }
+            nameToProperties.put(name, properties);
+        }
+    }
+
+    public void unregister(ObjectName name) throws ServiceNotFoundException {
+        String domainName = name.getDomain();
+        synchronized (this) {
+            registry.remove(name);
+
+            // just leave the an empty nameToProperty map
+            Map nameToProperties = (Map) domainIndex.get(domainName);
+            if (nameToProperties != null) {
+                nameToProperties.remove(name);
+            }
+        }
+    }
+
+    public ServiceInstance getServiceInstance(ObjectName name) throws ServiceNotFoundException {
+        ServiceInstance serviceInstance;
+        synchronized (this) {
+            serviceInstance = (ServiceInstance) registry.get(name);
+        }
+        if (serviceInstance == null) {
+            throw new ServiceNotFoundException(name.getCanonicalName());
+        }
+        return serviceInstance;
+    }
+
+    public Set listServiceInstances(ObjectName pattern) {
+        Set serviceNames = listServiceNames(pattern);
+        HashSet serviceInstances = new HashSet(serviceNames.size());
+        synchronized (this) {
+            for (Iterator iterator = serviceNames.iterator(); iterator.hasNext();) {
+                ObjectName objectName = (ObjectName) iterator.next();
+                ServiceInstance serviceInstance = (ServiceInstance) registry.get(objectName);
+                if (serviceInstance != null) {
+                    serviceInstances.add(serviceInstance);
+                }
+            }
+        }
+        return serviceInstances;
+    }
+
+    public Set listServiceNames(ObjectName pattern) {
+        if (pattern == null) {
+            synchronized (this) {
+                return new HashSet(registry.keySet());
+            }
+        }
+
+        String patternDomain = pattern.getDomain();
+        if (patternDomain.length() == 0) {
+            patternDomain = defaultDomainName;
+        }
+
+        // work with a copy of the registry key set
+        List nameToProperties;
+        if (!pattern.isDomainPattern()) {
+            synchronized (this) {
+                // create an array list big enough to match all names... extra space is better than resizing
+                nameToProperties = new ArrayList(registry.size());
+
+                // find we are only matching one specific domain, so
+                // just grab it directly from the index
+                Map map = (Map) domainIndex.get(patternDomain);
+                if (map != null) {
+                    nameToProperties.addAll(map.entrySet());
+                }
+            }
+        } else if (patternDomain.equals("*")) {
+            // this
+            //  is very commmon, so support it directly
+            synchronized (this) {
+                // create an array list big enough to match all names... extra space is better than resizing
+                nameToProperties = new ArrayList(registry.size());
+
+                // find we are matching all domain, so just grab all of them directly
+                for (Iterator iterator = domainIndex.values().iterator(); iterator.hasNext();) {
+                    Map map = (Map) iterator.next();
+
+                    // we can just copy the entry set directly into the list we don't
+                    // have to worry about duplicates as the maps are mutually exclusive
+                    nameToProperties.addAll(map.entrySet());
+                }
+            }
+        } else {
+            String perl5Pattern = domainPatternToPerl5(patternDomain);
+            Pattern domainPattern = Pattern.compile(perl5Pattern);
+
+            synchronized (this) {
+                // create an array list big enough to match all names... extra space is better than resizing
+                nameToProperties = new ArrayList(registry.size());
+
+                // find all of the matching domains
+                for (Iterator iterator = domainIndex.entrySet().iterator(); iterator.hasNext();) {
+                    Map.Entry entry = (Map.Entry) iterator.next();
+                    String domain = (String) entry.getKey();
+                    if (domainPattern.matcher(domain).matches()) {
+                        // we can just copy the entry set directly into the list we don't
+                        // have to worry about duplicates as the maps are mutually exclusive
+                        Map map = (Map) entry.getValue();
+                        nameToProperties.addAll(map.entrySet());
+                    }
+                }
+            }
+        }
+
+        if (nameToProperties.isEmpty()) {
+            return Collections.EMPTY_SET;
+        }
+
+        // convert the pattern property list to a HashMap as it is not synchronized
+        Map patternProperties = new HashMap(pattern.getKeyPropertyList());
+        patternProperties.remove("*");
+        boolean isMatchAll = patternProperties.isEmpty();
+        boolean isPropertyPattern = pattern.isPropertyPattern();
+
+        Set matchingNames = new HashSet();
+        for (Iterator iterator = nameToProperties.iterator(); iterator.hasNext();) {
+            Map.Entry entry = (Map.Entry) iterator.next();
+            Map properties = (Map) entry.getValue();
+
+            if (isMatchAll) {
+                matchingNames.add(entry.getKey());
+            } else if (isPropertyPattern) {
+                if (properties.entrySet().containsAll(patternProperties.entrySet())) {
+                    matchingNames.add(entry.getKey());
+                }
+            } else {
+                if (properties.entrySet().equals(patternProperties.entrySet())) {
+                    matchingNames.add(entry.getKey());
+                }
+            }
+        }
+        return matchingNames;
+    }
+
+    private static String domainPatternToPerl5(String pattern) {
+        char[] patternCharacters = pattern.toCharArray();
+        StringBuffer buffer = new StringBuffer(2 * patternCharacters.length);
+        for (int position = 0; position < patternCharacters.length; position++) {
+            char character = patternCharacters[position];
+            switch (character) {
+                case '*':
+                    // replace '*' with '.*'
+                    buffer.append(".*");
+                    break;
+                case '?':
+                    // replace '?' with '.'
+                    buffer.append('.');
+                    break;
+                default:
+                    // escape any perl5 characters with '\'
+                    if (isPerl5MetaCharacter(character)) {
+                        buffer.append('\\');
+                    }
+                    buffer.append(character);
+                    break;
+            }
+        }
+
+        return buffer.toString();
+    }
+
+    private static boolean isPerl5MetaCharacter(char character) {
+        return ("'*?+[]()|^$.{}\\".indexOf(character) >= 0);
+    }
+}
diff --git a/kernel/src/java/org/gbean/kernel/simple/SimpleServiceFactory.java b/kernel/src/java/org/gbean/kernel/simple/SimpleServiceFactory.java
new file mode 100644
index 0000000..b12cabc
--- /dev/null
+++ b/kernel/src/java/org/gbean/kernel/simple/SimpleServiceFactory.java
@@ -0,0 +1,69 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.kernel.simple;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.gbean.service.ServiceContext;
+import org.gbean.service.ServiceFactory;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class SimpleServiceFactory implements ServiceFactory {
+    private final Object service;
+    private final Map dependencies = new HashMap();
+    private boolean enabled = true;
+
+    public SimpleServiceFactory(Object service) {
+        this.service = service;
+    }
+
+    public Map getDependencies() {
+        return dependencies;
+    }
+
+    public void addDependency(String name, Set patterns) {
+        dependencies.put(name, patterns);
+    }
+
+    public Object createService(ServiceContext serviceContext) throws Exception {
+        if (service instanceof SimpleLifecycle) {
+            ((SimpleLifecycle) service).start();
+        }
+        return service;
+    }
+
+    public void destroyService(ServiceContext serviceContext, Object service) {
+        if (service != this.service) {
+            throw new IllegalArgumentException("Wrong service instance");
+        }
+        if (service instanceof SimpleLifecycle) {
+            ((SimpleLifecycle) service).stop();
+        }
+    }
+
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+}
diff --git a/kernel/src/java/org/gbean/loader/LoadAllMain.java b/kernel/src/java/org/gbean/loader/LoadAllMain.java
new file mode 100644
index 0000000..e9783e7
--- /dev/null
+++ b/kernel/src/java/org/gbean/loader/LoadAllMain.java
@@ -0,0 +1,55 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.loader;
+
+import org.gbean.kernel.Kernel;
+import org.gbean.kernel.Main;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class LoadAllMain implements Main {
+    public Kernel kernel;
+    public Main next;
+
+    public Kernel getKernel() {
+        return kernel;
+    }
+
+    public void setKernel(Kernel kernel) {
+        this.kernel = kernel;
+    }
+
+    public Main getNext() {
+        return next;
+    }
+
+    public void setNext(Main next) {
+        this.next = next;
+    }
+
+    public void main(String[] args) {
+        for (int i = 0; i < args.length; i++) {
+            String location = args[i];
+            LoaderUtil.load(kernel, location);
+            LoaderUtil.verifyAllServicesRunning(kernel);
+        }
+        if (next != null) {
+            next.main(args);
+        }
+    }
+}
diff --git a/kernel/src/java/org/gbean/loader/Loader.java b/kernel/src/java/org/gbean/loader/Loader.java
new file mode 100644
index 0000000..1c504a4
--- /dev/null
+++ b/kernel/src/java/org/gbean/loader/Loader.java
@@ -0,0 +1,26 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.loader;
+
+import javax.management.ObjectName;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public interface Loader {
+    ObjectName load(String location);
+}
diff --git a/kernel/src/java/org/gbean/loader/LoaderUtil.java b/kernel/src/java/org/gbean/loader/LoaderUtil.java
new file mode 100644
index 0000000..208e1d8
--- /dev/null
+++ b/kernel/src/java/org/gbean/loader/LoaderUtil.java
@@ -0,0 +1,82 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.loader;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Set;
+import javax.management.ObjectName;
+
+import org.gbean.kernel.ServiceName;
+import org.gbean.kernel.Kernel;
+import org.gbean.kernel.ServiceNotFoundException;
+import org.gbean.kernel.runtime.ServiceState;
+import org.gbean.spring.FatalStartupError;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class LoaderUtil {
+    private static final ObjectName LOADER_NAME_QUERY = ServiceName.createName("*:j2eeType=Loader,*");
+
+    private LoaderUtil() {
+    }
+
+    public static void load(Kernel kernel, String location) {
+        Collection loaders = kernel.listServices(LOADER_NAME_QUERY);
+        if (loaders.isEmpty()) {
+            throw new FatalStartupError("No loaders avalible in kernel");
+        }
+
+        for (Iterator iterator = loaders.iterator(); iterator.hasNext();) {
+            Loader loader = (Loader) iterator.next();
+            try {
+                ObjectName rootConfigurationName = loader.load(location);
+                if (rootConfigurationName != null) {
+                    kernel.startRecursiveService(rootConfigurationName);
+                    return;
+                }
+            } catch (Exception e) {
+                throw new FatalStartupError("Error loading '" + location + "' with " + loader, e);
+            }
+        }
+
+        String message = "No loaders were able to load '" + location + "' : Available loaders ";
+        for (Iterator iterator = loaders.iterator(); iterator.hasNext();) {
+            message += iterator.next();
+            if (iterator.hasNext()) {
+                message += ", ";
+            }
+        }
+        throw new FatalStartupError(message);
+    }
+
+    public static void verifyAllServicesRunning(Kernel kernel) {
+        Set allServices = kernel.listServiceNames(ServiceName.createName("*:*"));
+        for (Iterator iterator = allServices.iterator(); iterator.hasNext();) {
+            ObjectName objectName = (ObjectName) iterator.next();
+            try {
+                int state = kernel.getServiceState(objectName);
+                if (state != ServiceState.RUNNING_INDEX) {
+                    throw new FatalStartupError("Service '" + objectName + "' failed to start");
+                }
+            } catch (ServiceNotFoundException e) {
+                throw new FatalStartupError("Service '" + objectName + "' was unloaded");
+            }
+        }
+    }
+}
diff --git a/kernel/src/java/org/gbean/metadata/ClassMetadata.java b/kernel/src/java/org/gbean/metadata/ClassMetadata.java
new file mode 100644
index 0000000..ecb6d38
--- /dev/null
+++ b/kernel/src/java/org/gbean/metadata/ClassMetadata.java
@@ -0,0 +1,53 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.metadata;
+
+import java.util.Set;
+import java.util.Map;
+import java.util.Collection;
+import java.lang.reflect.Method;
+import java.lang.reflect.Constructor;
+
+import org.gbean.kernel.OperationSignature;
+import org.gbean.kernel.ConstructorSignature;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public interface ClassMetadata {
+    Class getType();
+
+    Set getMethods();
+
+    MethodMetadata getMethod(OperationSignature signature);
+
+    MethodMetadata getMethod(Method method);
+
+    Set getConstructors();
+
+    ConstructorMetadata getConstructor(ConstructorSignature signature);
+
+    ConstructorMetadata getConstructor(Constructor constructor);
+
+    Map getProperties();
+
+    Object get(Object key);
+
+    Object put(Object key, Object value);
+
+    Object remove(Object key);
+}
diff --git a/kernel/src/java/org/gbean/metadata/ConstructorMetadata.java b/kernel/src/java/org/gbean/metadata/ConstructorMetadata.java
new file mode 100644
index 0000000..4345fbd
--- /dev/null
+++ b/kernel/src/java/org/gbean/metadata/ConstructorMetadata.java
@@ -0,0 +1,44 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.metadata;
+
+import java.lang.reflect.Constructor;
+import java.util.List;
+import java.util.Map;
+
+import org.gbean.kernel.ConstructorSignature;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public interface ConstructorMetadata {
+    ConstructorSignature getSignature();
+
+    Constructor getConstructor();
+
+    List getParameters();
+
+    ParameterMetadata getParameter(int index);
+
+    Map getProperties();
+
+    Object get(Object key);
+
+    Object put(Object key, Object value);
+
+    Object remove(Object key);
+}
diff --git a/kernel/src/java/org/gbean/metadata/MetadataManager.java b/kernel/src/java/org/gbean/metadata/MetadataManager.java
new file mode 100644
index 0000000..291b37d
--- /dev/null
+++ b/kernel/src/java/org/gbean/metadata/MetadataManager.java
@@ -0,0 +1,24 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.metadata;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public interface MetadataManager {
+    ClassMetadata getClassMetadata(Class type);
+}
diff --git a/kernel/src/java/org/gbean/metadata/MetadataProvider.java b/kernel/src/java/org/gbean/metadata/MetadataProvider.java
new file mode 100644
index 0000000..befe6ed
--- /dev/null
+++ b/kernel/src/java/org/gbean/metadata/MetadataProvider.java
@@ -0,0 +1,24 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.metadata;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public interface MetadataProvider {
+    void addClassMetadata(ClassMetadata classMetadata);
+}
diff --git a/kernel/src/java/org/gbean/metadata/MethodMetadata.java b/kernel/src/java/org/gbean/metadata/MethodMetadata.java
new file mode 100644
index 0000000..e57f82b
--- /dev/null
+++ b/kernel/src/java/org/gbean/metadata/MethodMetadata.java
@@ -0,0 +1,44 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.metadata;
+
+import java.util.Map;
+import java.util.List;
+import java.lang.reflect.Method;
+
+import org.gbean.kernel.OperationSignature;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public interface MethodMetadata {
+    OperationSignature getSignature();
+
+    Method getMethod();
+
+    List getParameters();
+
+    ParameterMetadata getParameter(int index);
+
+    Map getProperties();
+
+    Object get(Object key);
+
+    Object put(Object key, Object value);
+
+    Object remove(Object key);
+}
diff --git a/kernel/src/java/org/gbean/metadata/ParameterMetadata.java b/kernel/src/java/org/gbean/metadata/ParameterMetadata.java
new file mode 100644
index 0000000..c3c4492
--- /dev/null
+++ b/kernel/src/java/org/gbean/metadata/ParameterMetadata.java
@@ -0,0 +1,36 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.metadata;
+
+import java.util.Map;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public interface ParameterMetadata {
+    int getIndex();
+
+    Class getType();
+
+    Map getProperties();
+
+    Object get(Object key);
+
+    Object put(Object key, Object value);
+
+    Object remove(Object key);
+}
diff --git a/kernel/src/java/org/gbean/metadata/simple/PropertiesMetadataProvider.java b/kernel/src/java/org/gbean/metadata/simple/PropertiesMetadataProvider.java
new file mode 100644
index 0000000..69f32a8
--- /dev/null
+++ b/kernel/src/java/org/gbean/metadata/simple/PropertiesMetadataProvider.java
@@ -0,0 +1,216 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.metadata.simple;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.LineNumberReader;
+import java.io.InputStreamReader;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.gbean.kernel.ClassLoading;
+import org.gbean.kernel.OperationSignature;
+import org.gbean.kernel.ConstructorSignature;
+import org.gbean.metadata.ClassMetadata;
+import org.gbean.metadata.MetadataProvider;
+import org.gbean.metadata.MethodMetadata;
+import org.gbean.metadata.ParameterMetadata;
+import org.gbean.metadata.ConstructorMetadata;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class PropertiesMetadataProvider implements MetadataProvider {
+    private static final Log log = LogFactory.getLog(PropertiesMetadataProvider.class);
+
+    public void addClassMetadata(ClassMetadata classMetadata) {
+        try {
+            Properties properties = loadProperties(classMetadata.getType());
+            for (Iterator iterator = properties.entrySet().iterator(); iterator.hasNext();) {
+                Map.Entry entry = (Map.Entry) iterator.next();
+                String propertyName = (String) entry.getKey();
+                String propertyValue = (String) entry.getValue();
+                processProperty(classMetadata, propertyName, propertyValue);
+            }
+        } catch (Exception e) {
+            log.error("Error while loading properties based metadata for class " + classMetadata.getType().getName());
+        }
+    }
+
+    private Properties loadProperties(Class type) throws IOException {
+        InputStream in = null;
+        try {
+            Properties properties = new Properties();
+            in = type.getClassLoader().getResourceAsStream(type.getName().replace('.', '/') + ".properties");
+            if (in != null) {
+                LineNumberReader lineReader = new LineNumberReader(new InputStreamReader(in));
+
+                String line;
+                while ((line = lineReader.readLine()) != null) {
+                    // todo allow line continuations with trailing '\'
+
+                    String name = line;
+                    String value = "";
+
+                    // break the line only at an equal sign
+                    int equals = line.indexOf('=');
+                    if (equals > 0) {
+                        name = line.substring(0, equals);
+                        if (equals < line.length()) {
+                            value = line.substring(equals + 1);
+                        }
+                    }
+
+                    // todo remove standard escapes such at \t \r \n and \\
+
+                    name = name.trim();
+                    value = value.trim();
+
+                    if (name.length() > 0 && name.charAt(0) != '#' && name.charAt(0) != '!') {
+                        properties.setProperty(name, value);
+                    }
+                }
+            }
+            return properties;
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (IOException e) {
+                    log.error("Error while closing properties based metadata input stream for class "  + type.getName());
+                }
+            }
+        }
+    }
+
+    private void processProperty(ClassMetadata classMetadata, String propertyName, String propertyValue) {
+        // try to parse the property name as a method property
+        if (parseMethodProperty(classMetadata, propertyName, propertyValue)) {
+            return;
+        }
+        classMetadata.put(propertyName, propertyValue);
+    }
+
+    private boolean parseMethodProperty(ClassMetadata classMetadata, String propertyName, String propertyValue) {
+        // if we don't have an open paren it is not a method property
+        int openParen = propertyName.indexOf('(');
+        if (openParen <= 0) {
+            return false;
+        }
+        // if we don't have a close paren followed by a period after the open paren it is not a method property
+        int closeParen = propertyName.indexOf(").", openParen);
+        if (closeParen < 0) {
+            return false;
+        }
+
+        // we must have some characters after the paren period
+        if (propertyName.length() <= closeParen + 2) {
+            return false;
+        }
+
+        // the method name is the characters before the openparen
+        String methodName = propertyName.substring(0, openParen).trim();
+
+        // parse the parameters
+        List params = parseMethodParameters(classMetadata.getType().getClassLoader(), propertyName.substring(openParen+1, closeParen));
+        if (params == null) {
+            return false;
+        }
+
+        // the property name of the method metadata is the stuff after the ")."
+        String methodPropertyName = propertyName.substring(closeParen + 2).trim();
+        if (methodPropertyName.length() == 0) {
+            return false;
+        }
+
+        // get the parameter index number if one is present
+        int parameterIndex = -1;
+        String parameterPropertyName = null;
+        try {
+            // if the property name does not include a period it is not metadata
+            int period = methodPropertyName.indexOf('.');
+            if (period > 0) {
+                // we must have some characters after the period
+                if (methodPropertyName.length() > period) {
+                    String indexString = methodPropertyName.substring(0, period);
+                    parameterIndex = Integer.parseInt(indexString);
+                    parameterPropertyName = methodPropertyName.substring(period + 1);
+                }
+            }
+        } catch (NumberFormatException e) {
+        }
+
+
+        // now that we know the method name and parameters lets try to get the method metadata for it
+        String className = classMetadata.getType().getName();
+        if (methodName.equals(className.substring(className.lastIndexOf(".") + 1))) {
+            ConstructorSignature signature = new ConstructorSignature(params);
+            ConstructorMetadata constructorMetadata = classMetadata.getConstructor(signature);
+            if (constructorMetadata == null) {
+                return false;
+            }
+
+            if (0 <= parameterIndex && parameterIndex < constructorMetadata.getSignature().getParameterTypes().size()) {
+                // this is parameter metadata
+                ParameterMetadata parameterMetadata = constructorMetadata.getParameter(parameterIndex);
+                parameterMetadata.put(parameterPropertyName, propertyValue);
+            } else {
+                // this is constructor metadata
+                constructorMetadata.put(methodPropertyName, propertyValue);
+            }
+        } else {
+            OperationSignature signature = new OperationSignature(methodName, params);
+            MethodMetadata methodMetadata = classMetadata.getMethod(signature);
+            if (methodMetadata == null) {
+                return false;
+            }
+
+            if (0 <= parameterIndex && parameterIndex < methodMetadata.getSignature().getParameterTypes().size()) {
+                // this is parameter metadata
+                ParameterMetadata parameterMetadata = methodMetadata.getParameter(parameterIndex);
+                parameterMetadata.put(parameterPropertyName, propertyValue);
+            } else {
+                // this is constructor metadata
+                methodMetadata.put(methodPropertyName, propertyValue);
+            }
+        }
+        return true;
+    }
+
+    private List parseMethodParameters(ClassLoader classLoader, String paramsString) {
+        try {
+            List parameters = new LinkedList();
+            for (StringTokenizer stringTokenizer = new StringTokenizer(paramsString, ", \t\n"); stringTokenizer.hasMoreTokens();) {
+                String parameter = stringTokenizer.nextToken();
+                Class parameterType = ClassLoading.loadClass(parameter, classLoader);
+                parameters.add(parameterType.getName());
+            }
+
+            return parameters;
+        } catch (ClassNotFoundException e) {
+            log.error("Unable to load method parameter class"  + e);
+            return null;
+        }
+    }
+}
diff --git a/kernel/src/java/org/gbean/metadata/simple/SimpleClassMetadata.java b/kernel/src/java/org/gbean/metadata/simple/SimpleClassMetadata.java
new file mode 100644
index 0000000..2218ca0
--- /dev/null
+++ b/kernel/src/java/org/gbean/metadata/simple/SimpleClassMetadata.java
@@ -0,0 +1,106 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.metadata.simple;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.gbean.kernel.ConstructorSignature;
+import org.gbean.kernel.OperationSignature;
+import org.gbean.metadata.ClassMetadata;
+import org.gbean.metadata.ConstructorMetadata;
+import org.gbean.metadata.MethodMetadata;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class SimpleClassMetadata implements ClassMetadata {
+    private final Map properties = new LinkedHashMap();
+    private final Class type;
+    private final Map methodMetadata = new HashMap();
+    private final Map constructorMetadata = new HashMap();
+
+    public SimpleClassMetadata(Class type) {
+        this.type = type;
+
+        Constructor[] constructors = type.getConstructors();
+        for (int i = 0; i < constructors.length; i++) {
+            Constructor constructor = constructors[i];
+            ConstructorSignature signature = new ConstructorSignature(constructor);
+            SimpleConstructorMetadata data = new SimpleConstructorMetadata(constructor);
+            constructorMetadata.put(signature, data);
+        }
+
+        Method[] methods = type.getMethods();
+        for (int i = 0; i < methods.length; i++) {
+            Method method = methods[i];
+            OperationSignature signature = new OperationSignature(method);
+            MethodMetadata data = new SimpleMethodMetadata(method);
+            methodMetadata.put(signature, data);
+        }
+    }
+
+    public Class getType() {
+        return type;
+    }
+
+    public Set getConstructors() {
+        return new HashSet(constructorMetadata.values());
+    }
+
+    public ConstructorMetadata getConstructor(ConstructorSignature signature) {
+        return (ConstructorMetadata) constructorMetadata.get(signature);
+    }
+
+    public ConstructorMetadata getConstructor(Constructor constructor) {
+        return (ConstructorMetadata) constructorMetadata.get(new ConstructorSignature(constructor));
+    }
+
+    public Set getMethods() {
+        return new HashSet(methodMetadata.values());
+    }
+
+    public MethodMetadata getMethod(OperationSignature signature) {
+        return (MethodMetadata) methodMetadata.get(signature);
+    }
+
+    public MethodMetadata getMethod(Method method) {
+        OperationSignature signature = new OperationSignature(method);
+        return (MethodMetadata) methodMetadata.get(signature);
+    }
+
+    public Map getProperties() {
+        return properties;
+    }
+
+    public Object get(Object key) {
+        return properties.get(key);
+    }
+
+    public Object put(Object key, Object value) {
+        return properties.put(key, value);
+    }
+
+    public Object remove(Object key) {
+        return properties.remove(key);
+    }
+}
diff --git a/kernel/src/java/org/gbean/metadata/simple/SimpleConstructorMetadata.java b/kernel/src/java/org/gbean/metadata/simple/SimpleConstructorMetadata.java
new file mode 100644
index 0000000..2beff7f
--- /dev/null
+++ b/kernel/src/java/org/gbean/metadata/simple/SimpleConstructorMetadata.java
@@ -0,0 +1,82 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.metadata.simple;
+
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.gbean.kernel.ConstructorSignature;
+import org.gbean.metadata.ConstructorMetadata;
+import org.gbean.metadata.ParameterMetadata;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class SimpleConstructorMetadata implements ConstructorMetadata {
+    private final Map properties = new LinkedHashMap();
+    private final Constructor constructor;
+    private final ConstructorSignature signature;
+    private final List parameterMetadata;
+
+    public SimpleConstructorMetadata(Constructor constructor) {
+        this.constructor = constructor;
+        this.signature = new ConstructorSignature(constructor);
+        Class[] parameterTypes = constructor.getParameterTypes();
+        List parameters = new ArrayList(parameterTypes.length);
+        for (int i = 0; i < parameterTypes.length; i++) {
+            Class parameterType = constructor.getParameterTypes()[i];
+            parameters.add(new SimpleParameterMetadata(i, parameterType));
+        }
+        parameterMetadata = Collections.unmodifiableList(parameters);
+    }
+
+    public Constructor getConstructor() {
+        return constructor;
+    }
+
+    public ConstructorSignature getSignature() {
+        return signature;
+    }
+
+    public List getParameters() {
+        return parameterMetadata;
+    }
+
+    public ParameterMetadata getParameter(int index) {
+        return (ParameterMetadata) parameterMetadata.get(index);
+    }
+
+    public Map getProperties() {
+        return properties;
+    }
+
+    public Object get(Object key) {
+        return properties.get(key);
+    }
+
+    public Object put(Object key, Object value) {
+        return properties.put(key, value);
+    }
+
+    public Object remove(Object key) {
+        return properties.remove(key);
+    }
+}
diff --git a/kernel/src/java/org/gbean/metadata/simple/SimpleMetadataManager.java b/kernel/src/java/org/gbean/metadata/simple/SimpleMetadataManager.java
new file mode 100644
index 0000000..c670445
--- /dev/null
+++ b/kernel/src/java/org/gbean/metadata/simple/SimpleMetadataManager.java
@@ -0,0 +1,61 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.metadata.simple;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.HashSet;
+
+import org.gbean.metadata.ClassMetadata;
+import org.gbean.metadata.MetadataManager;
+import org.gbean.metadata.MetadataProvider;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class SimpleMetadataManager implements MetadataManager {
+    private Collection metadataProviders;
+
+    public SimpleMetadataManager() {
+        metadataProviders = new HashSet();
+    }
+
+    public SimpleMetadataManager(Collection metadataProviders) {
+        this.metadataProviders = metadataProviders;
+    }
+
+    public Collection getMetadataProviders() {
+        return metadataProviders;
+    }
+
+    public void setMetadataProviders(Collection metadataProviders) {
+        this.metadataProviders = metadataProviders;
+    }
+
+    public void addMetadataProvider(MetadataProvider metadataProvider) {
+        metadataProviders.add(metadataProvider);
+    }
+
+    public ClassMetadata getClassMetadata(Class type) {
+        ClassMetadata classMetadata = new SimpleClassMetadata(type);
+        for (Iterator iterator = metadataProviders.iterator(); iterator.hasNext();) {
+            MetadataProvider metadataProvider = (MetadataProvider) iterator.next();
+            metadataProvider.addClassMetadata(classMetadata);
+        }
+        return classMetadata;
+    }
+}
\ No newline at end of file
diff --git a/kernel/src/java/org/gbean/metadata/simple/SimpleMethodMetadata.java b/kernel/src/java/org/gbean/metadata/simple/SimpleMethodMetadata.java
new file mode 100644
index 0000000..a1b7b0e
--- /dev/null
+++ b/kernel/src/java/org/gbean/metadata/simple/SimpleMethodMetadata.java
@@ -0,0 +1,82 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.metadata.simple;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.gbean.kernel.OperationSignature;
+import org.gbean.metadata.MethodMetadata;
+import org.gbean.metadata.ParameterMetadata;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class SimpleMethodMetadata implements MethodMetadata {
+    private final Map properties = new LinkedHashMap();
+    private final Method method;
+    private final OperationSignature signature;
+    private final List parameterMetadata;
+
+    public SimpleMethodMetadata(Method method) {
+        this.method = method;
+        this.signature = new OperationSignature(method);
+        Class[] parameterTypes = method.getParameterTypes();
+        List parameters = new ArrayList(parameterTypes.length);
+        for (int i = 0; i < parameterTypes.length; i++) {
+            Class parameterType = method.getParameterTypes()[i];
+            parameters.add(new SimpleParameterMetadata(i, parameterType));
+        }
+        parameterMetadata = Collections.unmodifiableList(parameters);
+    }
+
+    public Method getMethod() {
+        return method;
+    }
+
+    public OperationSignature getSignature() {
+        return signature;
+    }
+
+    public List getParameters() {
+        return parameterMetadata;
+    }
+
+    public ParameterMetadata getParameter(int index) {
+        return (ParameterMetadata) parameterMetadata.get(index);
+    }
+
+    public Map getProperties() {
+        return properties;
+    }
+
+    public Object get(Object key) {
+        return properties.get(key);
+    }
+
+    public Object put(Object key, Object value) {
+        return properties.put(key, value);
+    }
+
+    public Object remove(Object key) {
+        return properties.remove(key);
+    }
+}
diff --git a/kernel/src/java/org/gbean/metadata/simple/SimpleParameterMetadata.java b/kernel/src/java/org/gbean/metadata/simple/SimpleParameterMetadata.java
new file mode 100644
index 0000000..3fb961f
--- /dev/null
+++ b/kernel/src/java/org/gbean/metadata/simple/SimpleParameterMetadata.java
@@ -0,0 +1,60 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.metadata.simple;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.gbean.metadata.ParameterMetadata;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class SimpleParameterMetadata implements ParameterMetadata {
+    private final Map properties = new LinkedHashMap();
+    private final int index;
+    private final Class type;
+
+    public SimpleParameterMetadata(int index, Class type) {
+        this.index = index;
+        this.type = type;
+    }
+
+    public int getIndex() {
+        return index;
+    }
+
+    public Class getType() {
+        return type;
+    }
+
+    public Map getProperties() {
+        return properties;
+    }
+
+    public Object get(Object key) {
+        return properties.get(key);
+    }
+
+    public Object put(Object key, Object value) {
+        return properties.put(key, value);
+    }
+
+    public Object remove(Object key) {
+        return properties.remove(key);
+    }
+}
diff --git a/kernel/src/java/org/gbean/propertyeditor/InetAddressEditor.java b/kernel/src/java/org/gbean/propertyeditor/InetAddressEditor.java
new file mode 100644
index 0000000..3219d96
--- /dev/null
+++ b/kernel/src/java/org/gbean/propertyeditor/InetAddressEditor.java
@@ -0,0 +1,40 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.propertyeditor;
+
+import java.beans.PropertyEditorSupport;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class InetAddressEditor extends PropertyEditorSupport {
+    public void setAsText(String value) throws IllegalArgumentException {
+        try {
+            setValue(InetAddress.getByName(value));
+        } catch (UnknownHostException e) {
+            throw (IllegalArgumentException) new IllegalArgumentException().initCause(e);
+        }
+    }
+
+    public String getAsText() {
+        InetAddress inetAddress = ((InetAddress) getValue());
+        String text = inetAddress.toString();
+        return text;
+    }
+}
\ No newline at end of file
diff --git a/kernel/src/java/org/gbean/propertyeditor/ObjectNameEditor.java b/kernel/src/java/org/gbean/propertyeditor/ObjectNameEditor.java
new file mode 100644
index 0000000..bfffe96
--- /dev/null
+++ b/kernel/src/java/org/gbean/propertyeditor/ObjectNameEditor.java
@@ -0,0 +1,40 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.propertyeditor;
+
+import java.beans.PropertyEditorSupport;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class ObjectNameEditor extends PropertyEditorSupport {
+    public void setAsText(String value) throws IllegalArgumentException {
+        try {
+            setValue(new ObjectName(value));
+        } catch (MalformedObjectNameException e) {
+            throw (IllegalArgumentException) new IllegalArgumentException().initCause(e);
+        }
+    }
+
+    public String getAsText() {
+        ObjectName objectName = ((ObjectName) getValue());
+        String text = objectName.getCanonicalName();
+        return text;
+    }
+}
\ No newline at end of file
diff --git a/kernel/src/java/org/gbean/propertyeditor/URIEditor.java b/kernel/src/java/org/gbean/propertyeditor/URIEditor.java
new file mode 100644
index 0000000..5395022
--- /dev/null
+++ b/kernel/src/java/org/gbean/propertyeditor/URIEditor.java
@@ -0,0 +1,40 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.propertyeditor;
+
+import java.beans.PropertyEditorSupport;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class URIEditor extends PropertyEditorSupport {
+    public void setAsText(String value) throws IllegalArgumentException {
+        try {
+            setValue(new URI(value));
+        } catch (URISyntaxException e) {
+            throw (IllegalArgumentException) new IllegalArgumentException().initCause(e);
+        }
+    }
+
+    public String getAsText() {
+        URI uri = ((URI) getValue());
+        String text = uri.toString();
+        return text;
+    }
+}
\ No newline at end of file
diff --git a/kernel/src/java/org/gbean/proxy/ProxyFactory.java b/kernel/src/java/org/gbean/proxy/ProxyFactory.java
new file mode 100644
index 0000000..d6cb392
--- /dev/null
+++ b/kernel/src/java/org/gbean/proxy/ProxyFactory.java
@@ -0,0 +1,30 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.proxy;
+
+import javax.management.ObjectName;
+
+import org.gbean.kernel.ServiceNotFoundException;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public interface ProxyFactory {
+    Object createProxy(ObjectName target) throws ServiceNotFoundException;
+
+    Object createProxy(ObjectName target, Object data) throws ServiceNotFoundException;
+}
diff --git a/kernel/src/java/org/gbean/proxy/ProxyManager.java b/kernel/src/java/org/gbean/proxy/ProxyManager.java
new file mode 100644
index 0000000..45b1476
--- /dev/null
+++ b/kernel/src/java/org/gbean/proxy/ProxyManager.java
@@ -0,0 +1,138 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.proxy;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
+import javax.management.ObjectName;
+
+import net.sf.cglib.proxy.Callback;
+import net.sf.cglib.proxy.CallbackFilter;
+import net.sf.cglib.proxy.Enhancer;
+import net.sf.cglib.proxy.MethodInterceptor;
+import net.sf.cglib.proxy.NoOp;
+import org.gbean.kernel.Kernel;
+import org.gbean.kernel.ServiceName;
+import org.gbean.kernel.ServiceNotFoundException;
+import org.gbean.reflect.ServiceInvoker;
+import org.gbean.reflect.ServiceInvokerManager;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class ProxyManager {
+    private static ObjectName PROXY_MANAGER_QUERY = ServiceName.createName("*:j2eeType=ProxyManager,*");
+    public static ProxyManager findProxyManager(Kernel kernel) throws ServiceNotFoundException {
+        Set names = kernel.listServiceNames(PROXY_MANAGER_QUERY);
+        if (names.isEmpty()) {
+            throw new IllegalStateException("Proxy mananger could not be found in kernel: " + PROXY_MANAGER_QUERY);
+        }
+        if (names.size() > 1) {
+            throw new IllegalStateException("More then one proxy manangers were found in kernel: " + PROXY_MANAGER_QUERY);
+        }
+        ObjectName proxyManagerName = (ObjectName) names.iterator().next();
+        return (ProxyManager) kernel.getService(proxyManagerName);
+    }
+
+    private final ServiceInvokerManager serviceInvokerManager;
+    private final Map interceptors = new WeakHashMap();
+
+    public ProxyManager(ServiceInvokerManager serviceInvokerManager) {
+        this.serviceInvokerManager = serviceInvokerManager;
+    }
+
+    public synchronized ProxyFactory createProxyFactory(Class type) {
+        assert type != null: "type is null";
+        return new FastProxyFactory(type);
+    }
+
+    public synchronized Object createProxy(ObjectName target, Class type) throws ServiceNotFoundException {
+        return createProxy(target, type, null);
+    }
+
+    public synchronized Object createProxy(ObjectName target, Class type, Object data) throws ServiceNotFoundException {
+        assert type != null: "type is null";
+        assert target != null: "target is null";
+
+        return new FastProxyFactory(type).createProxy(target, data);
+    }
+
+    public boolean isProxy(Object proxy) {
+        return interceptors.containsKey(proxy);
+    }
+
+    public synchronized ObjectName getProxyTarget(Object proxy) {
+        ProxyMethodInterceptor methodInterceptor = (ProxyMethodInterceptor) interceptors.get(proxy);
+        if (methodInterceptor == null) {
+            return null;
+        }
+        return methodInterceptor.getObjectName();
+    }
+
+    public synchronized Object getProxyData(Object proxy) {
+        ProxyMethodInterceptor methodInterceptor = (ProxyMethodInterceptor) interceptors.get(proxy);
+        if (methodInterceptor == null) {
+            return null;
+        }
+        return methodInterceptor.getData();
+    }
+
+    private class FastProxyFactory implements ProxyFactory {
+        private final Class type;
+        private final Enhancer enhancer;
+
+        public FastProxyFactory(Class type) {
+            enhancer = new Enhancer();
+            enhancer.setSuperclass(type);
+            enhancer.setCallbackTypes(new Class[]{NoOp.class, MethodInterceptor.class});
+            enhancer.setCallbackFilter(FILTER);
+            enhancer.setUseFactory(false);
+            this.type = enhancer.createClass();
+        }
+
+        public synchronized Object createProxy(ObjectName target) throws ServiceNotFoundException {
+            return createProxy(target, null);
+        }
+
+        public synchronized Object createProxy(ObjectName target, Object data) throws ServiceNotFoundException {
+            assert target != null: "target is null";
+
+            ServiceInvoker serviceInvoker = serviceInvokerManager.getServiceInvoker(target);
+            ProxyMethodInterceptor interceptor = new ProxyMethodInterceptor(type, serviceInvoker, target, data);
+
+            // @todo trap CodeGenerationException indicating missing no-arg ctr
+            enhancer.setCallbacks(new Callback[]{NoOp.INSTANCE, interceptor});
+            Object proxy = enhancer.create();
+
+            interceptors.put(proxy, interceptor);
+            return proxy;
+        }
+    }
+
+    private static final CallbackFilter FILTER = new CallbackFilter() {
+        public int accept(Method method) {
+            if (method.getName().equals("finalize") &&
+                method.getParameterTypes().length == 0 &&
+                method.getReturnType() == Void.TYPE) {
+                return 0;
+            }
+            return 1;
+        }
+    };
+}
diff --git a/kernel/src/java/org/gbean/proxy/ProxyMethodInterceptor.java b/kernel/src/java/org/gbean/proxy/ProxyMethodInterceptor.java
new file mode 100644
index 0000000..38829a3
--- /dev/null
+++ b/kernel/src/java/org/gbean/proxy/ProxyMethodInterceptor.java
@@ -0,0 +1,185 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.proxy;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Iterator;
+import javax.management.ObjectName;
+
+import net.sf.cglib.asm.Type;
+import net.sf.cglib.core.Signature;
+import net.sf.cglib.proxy.MethodInterceptor;
+import net.sf.cglib.proxy.MethodProxy;
+import net.sf.cglib.reflect.FastClass;
+import org.gbean.kernel.OperationSignature;
+import org.gbean.reflect.OperationInvoker;
+import org.gbean.reflect.ServiceInvoker;
+
+/**
+ * @version $Rev: 106345 $ $Date: 2004-11-23 12:37:03 -0800 (Tue, 23 Nov 2004) $
+ */
+public class ProxyMethodInterceptor implements MethodInterceptor {
+    /**
+     * Type of the proxy interface
+     */
+    private final Class proxyType;
+
+    /**
+     * The object name to which we are connected.
+     */
+    private final ObjectName objectName;
+
+    /**
+     * OperationInvokers indexed by interface method id
+     */
+    private final OperationInvoker[] operationIndex;
+
+    /**
+     * The service invoker used by this proxy
+     */
+    private final ServiceInvoker serviceInvoker;
+
+    private final Object data;
+
+    public ProxyMethodInterceptor(Class proxyType, ServiceInvoker serviceInvoker, ObjectName objectName, Object data) {
+        assert proxyType != null;
+        assert serviceInvoker != null;
+        assert objectName != null;
+
+        this.proxyType = proxyType;
+        this.serviceInvoker = serviceInvoker;
+        this.objectName = objectName;
+        this.data = data;
+
+        operationIndex = createOperationIndex();
+    }
+
+    public ObjectName getObjectName() {
+        return objectName;
+    }
+
+    public Object getData() {
+        return data;
+    }
+
+    public final Object intercept(final Object object, final Method method, final Object[] args, final MethodProxy proxy) throws Throwable {
+        int interfaceIndex = proxy.getSuperIndex();
+        OperationInvoker operationInvoker = this.operationIndex[interfaceIndex];
+        if (operationInvoker == null) {
+            throw new UnsupportedOperationException("No implementation method: objectName=" + objectName + ", method=" + method);
+        }
+
+        return operationInvoker.invoke(args);
+    }
+
+    private OperationInvoker[] createOperationIndex() {
+        List operationIndex = serviceInvoker.getOperationIndex();
+        Map operations = new HashMap(operationIndex.size());
+        for (Iterator iterator = operationIndex.iterator(); iterator.hasNext();) {
+            OperationInvoker operationInvoker = (OperationInvoker) iterator.next();
+            operations.put(operationInvoker.getSignature(), operationInvoker);
+        }
+
+        // build the method lookup table
+        FastClass fastClass = FastClass.create(proxyType);
+        OperationInvoker[] operationInvokers = new OperationInvoker[fastClass.getMaxIndex() + 1];
+        Method[] methods = proxyType.getMethods();
+        for (int i = 0; i < methods.length; i++) {
+            Method method = methods[i];
+            int interfaceIndex = getSuperIndex(proxyType, method);
+            if (interfaceIndex >= 0) {
+                operationInvokers[interfaceIndex] = (OperationInvoker) operations.get(new OperationSignature(method));
+            }
+        }
+
+        // handle equals, hashCode and toString directly here
+        try {
+            operationInvokers[getSuperIndex(proxyType, proxyType.getMethod("equals", new Class[]{Object.class}))] = new EqualsInvoke(this);
+            operationInvokers[getSuperIndex(proxyType, proxyType.getMethod("hashCode", null))] = new HashCodeInvoke(this);
+            operationInvokers[getSuperIndex(proxyType, proxyType.getMethod("toString", null))] = new ToStringInvoke(objectName, proxyType.getName());
+        } catch (Exception e) {
+            // this can not happen... all classes must implement equals, hashCode and toString
+            throw new AssertionError(e);
+        }
+
+        return operationInvokers;
+    }
+
+    private static int getSuperIndex(Class proxyType, Method method) {
+        Signature signature = new Signature(method.getName(), Type.getReturnType(method), Type.getArgumentTypes(method));
+        MethodProxy methodProxy = MethodProxy.find(proxyType, signature);
+        if (methodProxy != null) {
+            return methodProxy.getSuperIndex();
+        }
+        return -1;
+    }
+
+    static final class HashCodeInvoke implements OperationInvoker {
+        private final MethodInterceptor methodInterceptor;
+
+        public HashCodeInvoke(MethodInterceptor methodInterceptor) {
+            this.methodInterceptor = methodInterceptor;
+        }
+
+        public OperationSignature getSignature() {
+            return new OperationSignature("hashCode", new String[] {});
+        }
+
+        // todo this should be hashcode of objectname         
+        public Object invoke(Object[] arguments) {
+            return new Integer(methodInterceptor.hashCode());
+        }
+    }
+
+    static final class EqualsInvoke implements OperationInvoker {
+        private final MethodInterceptor methodInterceptor;
+
+        public EqualsInvoke(MethodInterceptor methodInterceptor) {
+            this.methodInterceptor = methodInterceptor;
+        }
+
+        public OperationSignature getSignature() {
+            return new OperationSignature("equals", new String[] {Object.class.getName()});
+        }
+
+        // todo this should do isProxy and compare the target objectname
+        public Object invoke(Object[] arguments) {
+            return Boolean.valueOf(methodInterceptor.equals(arguments[0]));
+        }
+    }
+
+    static final class ToStringInvoke implements OperationInvoker {
+        private final String interfaceName;
+        private final ObjectName objectName;
+
+        public ToStringInvoke(ObjectName objectName, String interfaceName) {
+            this.objectName = objectName;
+            this.interfaceName = "[" + interfaceName + ": ";
+        }
+
+        public OperationSignature getSignature() {
+            return new OperationSignature("toString", new String[] {});
+        }
+
+        public Object invoke(Object[] arguments) {
+            return interfaceName + objectName + "]";
+        }
+    }
+}
diff --git a/kernel/src/java/org/gbean/reflect/OperationInvoker.java b/kernel/src/java/org/gbean/reflect/OperationInvoker.java
new file mode 100644
index 0000000..1f7f182
--- /dev/null
+++ b/kernel/src/java/org/gbean/reflect/OperationInvoker.java
@@ -0,0 +1,27 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.reflect;
+
+import org.gbean.kernel.OperationSignature;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public interface OperationInvoker {
+    OperationSignature getSignature();
+    Object invoke(Object[] arguments) throws Exception;
+}
diff --git a/kernel/src/java/org/gbean/reflect/PropertyInvoker.java b/kernel/src/java/org/gbean/reflect/PropertyInvoker.java
new file mode 100644
index 0000000..1e8c2da
--- /dev/null
+++ b/kernel/src/java/org/gbean/reflect/PropertyInvoker.java
@@ -0,0 +1,33 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.reflect;
+
+import org.gbean.kernel.OperationSignature;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public interface PropertyInvoker {
+    String getPropertyName();
+    Class getType();
+    boolean isReadable();
+    OperationSignature getGetterSignature();
+    Object invokeGetter() throws Exception;
+    boolean isWritable();
+    OperationSignature getSetterSignature();
+    void invokeSetter(Object value) throws Exception;
+}
diff --git a/kernel/src/java/org/gbean/reflect/ServiceInvoker.java b/kernel/src/java/org/gbean/reflect/ServiceInvoker.java
new file mode 100644
index 0000000..188f88c
--- /dev/null
+++ b/kernel/src/java/org/gbean/reflect/ServiceInvoker.java
@@ -0,0 +1,716 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.reflect;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeSet;
+import javax.management.ObjectName;
+
+import net.sf.cglib.reflect.FastClass;
+import net.sf.cglib.reflect.FastMethod;
+import org.apache.geronimo.gbean.DynamicGBean;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.gbean.kernel.Kernel;
+import org.gbean.kernel.LifecycleAdapter;
+import org.gbean.kernel.OperationSignature;
+import org.gbean.kernel.ServiceNotFoundException;
+import org.gbean.kernel.NoSuchAttributeException;
+import org.gbean.kernel.NoSuchOperationException;
+import org.gbean.kernel.runtime.ServiceState;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class ServiceInvoker {
+    /**
+     * Our log stream
+     */
+    private static final Log log = LogFactory.getLog(ServiceInvoker.class);
+
+    /**
+     * The kernel in which the service is loaded
+     */
+    private final Kernel kernel;
+
+    /**
+     * The name of the service in the kernel
+     */
+    private final ObjectName name;
+
+    /**
+     * The listener that is notified when the service goes offline.
+     */
+    private final ServiceInvokerLifecycleListener lifecycleListener;
+
+    /**
+     * The actual target service
+     */
+    private Object serviceInstance;
+
+    /**
+     * Is the service currently running?
+     */
+    private boolean serviceRunning;
+
+    /**
+     * Property invokers
+     */
+    private PropertyInvokerImpl[] properties;
+
+    /**
+     * Property name to index number
+     */
+    private final Map propertyIndex = new HashMap();
+
+    /**
+     * Operations lookup table
+     */
+    private OperationInvokerImpl[] operations;
+
+    /**
+     * Operations supported by the service by OperationSignature
+     */
+    private final Map operationIndex = new HashMap();
+
+    /**
+     * Has the ServiceInvoker itself been started?
+     */
+    private boolean serviceInvokerStarted = false;
+
+    public ServiceInvoker(Kernel kernel, ObjectName name) {
+        this.kernel = kernel;
+        this.name = name;
+        lifecycleListener = new ServiceInvokerLifecycleListener();
+    }
+
+    public synchronized void start() throws ServiceNotFoundException {
+        if (serviceRunning) {
+            return;
+        }
+
+        kernel.addLifecycleListener(lifecycleListener, name);
+        serviceInvokerStarted = true;
+        assureRunning();
+    }
+
+    public synchronized void stop() {
+        kernel.removeLifecycleListener(lifecycleListener);
+        properties = null;
+        propertyIndex.clear();
+        operations = null;
+        operationIndex.clear();
+        serviceInstance = null;
+        serviceRunning = false;
+        serviceInvokerStarted = false;
+    }
+
+    public synchronized ObjectName getServiceName() {
+        return name;
+    }
+    
+    public synchronized Class getServiceType() {
+        assureRunning();
+        return serviceInstance.getClass();
+    }
+
+    public synchronized List getPropertyIndex() {
+        assureRunning();
+        return Collections.unmodifiableList(Arrays.asList(properties));
+    }
+
+    public synchronized List getOperationIndex() {
+        assureRunning();
+        return Collections.unmodifiableList(Arrays.asList(operations));
+    }
+
+    /**
+     * Gets the attribute value using the attribute index.  This is the most efficient way to get
+     * an attribute as it avoids a HashMap lookup.
+     *
+     * @param index the index of the attribute
+     * @return the attribute value
+     * @throws Exception if a target instance throws and exception
+     * @throws IndexOutOfBoundsException if the index is invalid
+     */
+    public Object getAttribute(int index) throws Exception {
+        // copy target into local variables from within a synchronized block to gaurentee a consistent read
+        Object instance;
+        PropertyInvokerImpl property;
+        synchronized (this) {
+            assureRunning();
+            instance = serviceInstance;
+            property = properties[index];
+        }
+
+        if (!property.isReadable()) {
+            throw new IllegalArgumentException("Property " + property.getPropertyName() + " is not readable");
+        }
+        try {
+            Object value = property.getterFastMethod.invoke(instance, null);
+            return value;
+        } catch (InvocationTargetException e) {
+            throw unwrap(e);
+        }
+    }
+
+    /**
+     * Gets an attribute's value by name.  This get style is less efficient becuse the attribute must
+     * first be looked up in a HashMap.
+     *
+     * @param name the name of the property to retrieve
+     * @return the property value
+     * @throws Exception if a problem occurs while getting the value
+     * @throws NoSuchAttributeException if the attribute name is not found in the map
+     */
+    public Object getAttribute(String name) throws NoSuchAttributeException, Exception {
+        // copy target into local variables from within a synchronized block to gaurentee a consistent read
+        Object instance;
+        PropertyInvokerImpl property = null;
+        synchronized (this) {
+            assureRunning();
+            instance = serviceInstance;
+            Integer index = (Integer) propertyIndex.get(name);
+            if (index != null) {
+                property = properties[index.intValue()];
+            }
+        }
+
+        if (property != null) {
+            if (!property.isReadable()) {
+                throw new IllegalArgumentException("Property " + property.getPropertyName() + " is not readable");
+            }
+            try {
+                Object value = property.getterFastMethod.invoke(instance, null);
+                return value;
+            } catch (InvocationTargetException e) {
+                throw unwrap(e);
+            }
+        } else if (instance instanceof DynamicGBean) {
+            Object value = ((DynamicGBean) instance).getAttribute(name);
+            return value;
+        }
+        throw new NoSuchAttributeException("Unknown property '" + name + "' in service " + name);
+    }
+
+    /**
+     * Sets the attribute value using the attribute index.  This is the most efficient way to set
+     * an attribute as it avoids a HashMap lookup.
+     *
+     * @param index the index of the attribute
+     * @param value the new value of attribute value
+     * @throws Exception if a target instance throws and exception
+     * @throws IndexOutOfBoundsException if the index is invalid
+     */
+    public void setAttribute(int index, Object value) throws Exception, IndexOutOfBoundsException {
+        // copy target into local variables from within a synchronized block to gaurentee a consistent read
+        Object instance;
+        PropertyInvokerImpl property;
+        synchronized (this) {
+            assureRunning();
+            instance = serviceInstance;
+            property = properties[index];
+        }
+
+        if (!property.isWritable()) {
+            throw new IllegalArgumentException("Property " + property.getPropertyName() + " is not writable");
+        }
+        try {
+            property.setterFastMethod.invoke(instance, new Object[] {value});
+        } catch (InvocationTargetException e) {
+            throw unwrap(e);
+        }
+    }
+
+    /**
+     * Sets an attribute's value by name.  This set style is less efficient becuse the attribute must
+     * first be looked up in a HashMap.
+     *
+     * @param attributeName the name of the attribute to retrieve
+     * @param attributeValue the new attribute value
+     * @throws Exception if a target instance throws and exception
+     * @throws NoSuchAttributeException if the attribute name is not found in the map
+     */
+    public void setAttribute(String attributeName, Object attributeValue) throws Exception, NoSuchAttributeException {
+        // copy target into local variables from within a synchronized block to gaurentee a consistent read
+        Object instance;
+        PropertyInvokerImpl property = null;
+        synchronized (this) {
+            assureRunning();
+            instance = serviceInstance;
+            Integer index = (Integer) propertyIndex.get(attributeName);
+            if (index != null) {
+                property = properties[index.intValue()];
+            }
+        }
+
+        if (property != null) {
+            if (!property.isWritable()) {
+                throw new IllegalArgumentException("Property " + property.getPropertyName() + " is not writable");
+            }
+            try {
+                property.setterFastMethod.invoke(instance, new Object[] {attributeValue});
+            } catch (InvocationTargetException e) {
+                throw unwrap(e);
+            }
+        } else if (instance instanceof DynamicGBean) {
+            ((DynamicGBean) instance).setAttribute(attributeName, attributeValue);
+        } else {
+            throw new NoSuchAttributeException("Unknown attribute '" + attributeName + "' in service " + name);
+        }
+    }
+
+    /**
+     * Invokes an opreation using the operation index.  This is the most efficient way to invoke
+     * an operation as it avoids a HashMap lookup.
+     *
+     * @param index the index of the attribute
+     * @param arguments the arguments to the operation
+     * @return the result of the operation
+     * @throws Exception if a target instance throws and exception
+     * @throws IndexOutOfBoundsException if the index is invalid
+     * @throws IllegalStateException if the service has been destroyed
+     */
+    public Object invoke(int index, Object[] arguments) throws Exception {
+        // copy target into local variables from within a synchronized block to gaurentee a consistent read
+        Object instance;
+        OperationInvokerImpl operation;
+        synchronized (this) {
+            assureRunning();
+            instance = serviceInstance;
+            operation = operations[index];
+        }
+
+        try {
+            Object value = operation.fastMethod.invoke(instance, arguments);
+            return value;
+        } catch (InvocationTargetException e) {
+            throw unwrap(e);
+        }
+    }
+
+    /**
+     * Invokes an operation on the service by method signature.  This style if invocation is
+     * inefficient, because the target method must be looked up in a hashmap using a freshly constructed
+     * OperationSignature object.
+     *
+     * @param operationName the name of the operation to invoke
+     * @param arguments arguments to the operation
+     * @param types types of the operation arguemtns
+     * @return the result of the operation
+     * @throws Exception if a target instance throws and exception
+     * @throws NoSuchOperationException if the operation signature is not found in the map
+     * @throws IllegalStateException if the service has been destroyed
+     */
+    public Object invoke(String operationName, Object[] arguments, String[] types) throws Exception, NoSuchOperationException {
+        // copy target into local variables from within a synchronized block to gaurentee a consistent read
+        Object instance;
+        OperationInvokerImpl operation;
+        synchronized (this) {
+            assureRunning();
+            instance = serviceInstance;
+
+            OperationSignature signature = new OperationSignature(operationName, types);
+            Integer index = (Integer) operationIndex.get(signature);
+            if (index == null) {
+                throw new NoSuchOperationException("Unknown operation " + signature);
+            }
+            operation = operations[index.intValue()];
+        }
+
+        try {
+            Object value = operation.fastMethod.invoke(instance, arguments);
+            return value;
+        } catch (InvocationTargetException e) {
+            throw unwrap(e);
+        }
+    }
+
+    public synchronized void assureRunning() {
+        if (!serviceRunning) {
+            try {
+                updateState();
+                if (!serviceRunning) {
+                    throw new IllegalStateException("Service must be in the running or stopping state: name=" + name + ", state=" + ServiceState.fromIndex(kernel.getServiceState(name)));
+                }
+            } catch (ServiceNotFoundException e) {
+                throw new IllegalStateException("Service is not loaded: " + name);
+            }
+        }
+    }
+
+    private synchronized void updateState() throws ServiceNotFoundException {
+        if (!serviceInvokerStarted) {
+            throw new IllegalStateException("Service invoker has not been started: name=" + name);
+        }
+        try {
+            serviceRunning = false;
+
+            // get the current state
+            int serviceState = kernel.getServiceState(name);
+
+            // we must be in a running state
+            boolean running = serviceState == ServiceState.RUNNING_INDEX || serviceState == ServiceState.STOPPING_INDEX;
+            if (running) {
+                if (serviceInstance == null) {
+                    serviceInstance = kernel.getService(name);
+
+                    // if we don't have a service instance something is wrong
+                    if (serviceInstance == null) {
+                        throw new IllegalStateException("Could not get service instance: name=" + name);
+                    }
+                    createIndex(serviceInstance.getClass());
+                }
+                serviceRunning = true;
+            }
+        } finally {
+            if (!serviceRunning) {
+                kernel.removeLifecycleListener(lifecycleListener);
+                serviceInvokerStarted = false;
+                properties = null;
+                propertyIndex.clear();
+                operations = null;
+                operationIndex.clear();
+                serviceInstance = null;
+            }
+        }
+    }
+
+    private class ServiceInvokerLifecycleListener extends LifecycleAdapter {
+        public void stopped(ObjectName objectName) {
+            try {
+                updateState();
+            } catch (Exception e) {
+                log.info("Unable to update service invoker for service " + name, e);
+            }
+        }
+
+        public void unloaded(ObjectName objectName) {
+            try {
+                updateState();
+            } catch (Exception e) {
+                log.info("Unable to update service invoker for service " + name, e);
+            }
+        }
+    }
+
+    private void createIndex(Class type) {
+        // attributes
+        Method[] methods = type.getMethods();
+
+        // map the getters
+        Map getterMap = new HashMap(methods.length);
+        for (int i = 0; i < methods.length; i++) {
+            Method method = methods[i];
+            String methodName = method.getName();
+            if (Modifier.isPublic(method.getModifiers()) &&
+                    !Modifier.isStatic(method.getModifiers()) &&
+                    method.getParameterTypes().length == 0 &&
+                    method.getReturnType() != Void.TYPE) {
+                if (methodName.length() > 3 && methodName.startsWith("get") && !methodName.equals("getClass")) {
+                    String attributeName = fixAttributeName(methodName.substring(3));
+
+                    // if this attribute also has an "is" accessor make sure the return type is boolean
+                    Method isAccessor = (Method) getterMap.get(attributeName);
+                    if (isAccessor != null && method.getReturnType() != Boolean.TYPE) {
+                        throw new IllegalArgumentException("Getter has both a get<name> and is<name> accessor but the getter return type is not boolean:" +
+                                " class=" + type.getName() +
+                                ", attribute=" + attributeName +
+                                ", getAccessorType=" + method.getReturnType().getName());
+                    }
+
+                    // add it
+                    getterMap.put(attributeName, method);
+                } else if (methodName.length() > 2 && methodName.startsWith("is")) {
+                    String attributeName = fixAttributeName(methodName.substring(2));
+
+                    // an is accessor must return boolean
+                    if (method.getReturnType() != Boolean.TYPE) {
+                        throw new IllegalArgumentException("An is<name> accessor must return boolean:" +
+                                " class=" + type.getName() +
+                                ", attribute=" + attributeName +
+                                ", attributeType=" + method.getReturnType().getName());
+                    }
+
+                    // if this attribute also has a "get" accessor make sure the getter return type is boolean
+                    Method getAccessor = (Method) getterMap.get(attributeName);
+                    if (getAccessor != null && method.getReturnType() != Boolean.TYPE) {
+                        throw new IllegalArgumentException("Getter has both a get<name> and is<name> accessor but the getter return type is not boolean:" +
+                                " class=" + type.getName() +
+                                ", attribute=" + attributeName +
+                                ", getAccessorType=" + getAccessor.getReturnType().getName());
+                    }
+
+                    // add it
+                    getterMap.put(attributeName, method);
+                }
+            }
+        }
+
+        // map the setters
+        Map setterMap = new HashMap(methods.length);
+        for (int i = 0; i < methods.length; i++) {
+            Method method = methods[i];
+            String methodName = method.getName();
+            if (Modifier.isPublic(method.getModifiers()) &&
+                    !Modifier.isStatic(method.getModifiers()) &&
+                    method.getParameterTypes().length == 1 &&
+                    method.getReturnType() == Void.TYPE &&
+                    methodName.length() > 3 &&
+                    methodName.startsWith("set")) {
+
+                String attributeName = fixAttributeName(methodName.substring(3));
+                if (setterMap.containsKey(attributeName)) {
+                    // this bean attributte has multiple setters with different types, so treat only as operations
+                    setterMap.put(attributeName, null);
+                } else {
+                    setterMap.put(attributeName, method);
+                }
+
+                // the getter and setter types must match
+                Method getterMethod = (Method) getterMap.get(attributeName);
+                if (getterMethod != null && !getterMethod.getReturnType().equals(method.getParameterTypes()[0])) {
+                    throw new IllegalArgumentException("Getter and setter types do not match:" +
+                            " class=" + type.getName() +
+                            ", attribute=" + attributeName +
+                            ", getAccessorType=" + getterMethod.getReturnType().getName() +
+                            ", setAccessorType=" + method.getParameterTypes()[0].getName());
+                }
+            }
+        }
+        // remove any setter with a null method (these setters have multiple methods with different types)
+        for (Iterator iterator = setterMap.entrySet().iterator(); iterator.hasNext();) {
+            Map.Entry entry = (Map.Entry) iterator.next();
+            Method setter = (Method) entry.getValue();
+            if (setter == null) {
+                iterator.remove();
+            }
+        }
+
+        TreeSet propertyNames = new TreeSet();
+        propertyNames.addAll(getterMap.keySet());
+        propertyNames.addAll(setterMap.keySet());
+
+        List propertyList = new ArrayList(getterMap.size());
+        for (Iterator iterator = propertyNames.iterator(); iterator.hasNext();) {
+            String propertyName = (String) iterator.next();
+            Method getter = (Method) getterMap.get(propertyName);
+            Method setter = (Method) setterMap.get(propertyName);
+            propertyIndex.put(propertyName, new Integer(propertyList.size()));
+            propertyList.add(new PropertyInvokerImpl(propertyName, getter, setter));
+        }
+        properties = (PropertyInvokerImpl[]) propertyList.toArray(new PropertyInvokerImpl[propertyList.size()]);
+
+        // operations
+        List operationList = new ArrayList(methods.length);
+        for (int i = 0; i < methods.length; i++) {
+            Method method = methods[i];
+            if (Modifier.isPublic(method.getModifiers()) && !Modifier.isStatic(method.getModifiers())) {
+                operationIndex.put(new OperationSignature(method), new Integer(operationList.size()));
+                operationList.add(new OperationInvokerImpl(method));
+            }
+        }
+        operations = (OperationInvokerImpl[]) operationList.toArray(new OperationInvokerImpl[operationList.size()]);
+    }
+
+    private static Exception unwrap(InvocationTargetException e) throws Exception {
+        Throwable cause = e.getTargetException();
+        if (cause instanceof Exception) {
+            return (Exception) cause;
+        } else if (cause instanceof Error) {
+            throw (Error) cause;
+        }
+        return e;
+    }
+
+    private static String fixAttributeName(String attributeName) {
+        if (Character.isUpperCase(attributeName.charAt(0))) {
+            return Character.toLowerCase(attributeName.charAt(0)) + attributeName.substring(1);
+        }
+        return attributeName;
+    }
+
+    private final class OperationInvokerImpl implements OperationInvoker {
+        private final OperationSignature signature;
+        private final FastMethod fastMethod;
+
+        public OperationInvokerImpl(Method method) {
+            assert method != null: "method is null";
+            this.signature = new OperationSignature(method);
+            this.fastMethod = FastClass.create(method.getDeclaringClass()).getMethod(method);
+        }
+
+        public OperationSignature getSignature() {
+            return signature;
+        }
+
+        public Object invoke(Object[] arguments) throws Exception {
+            Object instance;
+            synchronized (ServiceInvoker.this) {
+                assureRunning();
+                instance = serviceInstance;
+            }
+
+            try {
+                Object value = fastMethod.invoke(instance, arguments);
+                return value;
+            } catch (InvocationTargetException e) {
+                throw unwrap(e);
+            }
+        }
+
+        public int hashCode() {
+            return signature.hashCode();
+        }
+
+        public boolean equals(Object obj) {
+            if (!(obj instanceof OperationInvokerImpl)) {
+                return false;
+            }
+            OperationInvokerImpl operationInvoker = (OperationInvokerImpl)obj;
+            return signature.equals(operationInvoker.signature);
+        }
+
+        public String toString() {
+            return "[MethodInvoker: " + getSignature().toString() + "]";
+        }
+    }
+
+    public class PropertyInvokerImpl implements PropertyInvoker {
+        private final String propertyName;
+        private final Class type;
+        private final OperationSignature getterSignature;
+        private final FastMethod getterFastMethod;
+        private final OperationSignature setterSignature;
+        private final FastMethod setterFastMethod;
+
+        public PropertyInvokerImpl(String propertyName, Method getter, Method setter) {
+            assert propertyName != null: "propertyName is null";
+            assert getter != null || setter != null: "getter and setter are null";
+
+            this.propertyName = propertyName;
+
+            if (getter != null) {
+                type = getter.getReturnType();
+            } else {
+                type = setter.getParameterTypes()[0];
+            }
+
+            if (getter != null) {
+                getterSignature = new OperationSignature(getter);
+                getterFastMethod = FastClass.create(getter.getDeclaringClass()).getMethod(getter);
+            } else {
+                getterSignature = null;
+                getterFastMethod = null;
+            }
+
+            if (setter != null) {
+                setterSignature = new OperationSignature(setter);
+                setterFastMethod = FastClass.create(setter.getDeclaringClass()).getMethod(setter);
+            } else {
+                setterSignature = null;
+                setterFastMethod = null;
+            }
+        }
+
+        public String getPropertyName() {
+            return propertyName;
+        }
+
+        public Class getType() {
+            return type;
+        }
+
+        public boolean isReadable() {
+            return getterFastMethod != null;
+        }
+
+        public OperationSignature getGetterSignature() {
+            return getterSignature;
+        }
+
+        public Object invokeGetter() throws Exception {
+            // copy target into local variables from within a synchronized block to gaurentee a consistent read
+            Object instance;
+            synchronized (ServiceInvoker.this) {
+                assureRunning();
+                instance = serviceInstance;
+            }
+
+            if (!isReadable()) {
+                throw new IllegalArgumentException("Property " + propertyName + " is not readable");
+            }
+            try {
+                Object value = getterFastMethod.invoke(instance, null);
+                return value;
+            } catch (InvocationTargetException e) {
+                throw unwrap(e);
+            }
+        }
+
+        public boolean isWritable() {
+            return setterFastMethod != null;
+        }
+
+        public OperationSignature getSetterSignature() {
+            return setterSignature;
+        }
+
+        public void invokeSetter(Object value) throws Exception {
+            // copy target into local variables from within a synchronized block to gaurentee a consistent read
+            Object instance;
+            synchronized (ServiceInvoker.this) {
+                assureRunning();
+                instance = serviceInstance;
+            }
+
+            if (!isWritable()) {
+                throw new IllegalArgumentException("Property " + propertyName + " is not writable");
+            }
+            try {
+                setterFastMethod.invoke(instance, new Object[] {value});
+            } catch (InvocationTargetException e) {
+                throw unwrap(e);
+            }
+        }
+
+        public int hashCode() {
+            return propertyName.hashCode();
+        }
+
+        public boolean equals(Object obj) {
+            if (!(obj instanceof PropertyInvokerImpl)) {
+                return false;
+            }
+            PropertyInvokerImpl fastPropertyInvoker = (PropertyInvokerImpl)obj;
+            return propertyName.equals(fastPropertyInvoker.propertyName);
+        }
+
+        public String toString() {
+            return "[PropertySetter: name=" + propertyName + ", getter=" + getterSignature + ", setter=" + setterSignature + "]";
+        }
+    }
+}
diff --git a/kernel/src/java/org/gbean/reflect/ServiceInvokerManager.java b/kernel/src/java/org/gbean/reflect/ServiceInvokerManager.java
new file mode 100644
index 0000000..48ebf6d
--- /dev/null
+++ b/kernel/src/java/org/gbean/reflect/ServiceInvokerManager.java
@@ -0,0 +1,118 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.reflect;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import javax.management.ObjectName;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.gbean.kernel.Kernel;
+import org.gbean.kernel.LifecycleAdapter;
+import org.gbean.kernel.ServiceName;
+import org.gbean.kernel.ServiceNotFoundException;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class ServiceInvokerManager {
+    private static final Log log = LogFactory.getLog(ServiceInvokerManager.class);
+    private static final ObjectName ALL = ServiceName.createName("*:*");
+    private final Kernel kernel;
+    private final Map registry = new HashMap();
+    private final ServiceRegistrationListener lifecycleListener;
+
+    public ServiceInvokerManager(Kernel kernel) {
+        this.kernel = kernel;
+        lifecycleListener = new ServiceRegistrationListener();
+    }
+
+    public synchronized void start() {
+        kernel.addLifecycleListener(lifecycleListener, ALL);
+        Set allNames = kernel.listServiceNames(ALL);
+        for (Iterator iterator = allNames.iterator(); iterator.hasNext();) {
+            ObjectName objectName = (ObjectName) iterator.next();
+            try {
+                register(objectName);
+            } catch (Exception e) {
+                log.info("Unable to create service invoker for " + objectName, e);
+            }
+        }
+    }
+
+    public synchronized ServiceInvoker getServiceInvoker(ObjectName objectName) throws ServiceNotFoundException, IllegalStateException {
+        ServiceInvoker serviceInvoker = (ServiceInvoker) registry.get(objectName);
+        if (serviceInvoker != null) {
+            serviceInvoker.assureRunning();
+        } else {
+            register(objectName);
+            serviceInvoker = (ServiceInvoker) registry.get(objectName);
+        }
+
+        if (serviceInvoker == null) {
+            throw new ServiceNotFoundException(objectName.getCanonicalName());
+        }
+        return serviceInvoker;
+    }
+
+    public synchronized void stop() {
+        kernel.removeLifecycleListener(lifecycleListener);
+        registry.clear();
+    }
+
+    private void register(ObjectName objectName) throws ServiceNotFoundException, IllegalStateException {
+        synchronized (this) {
+            if (registry.containsKey(objectName)) {
+                return;
+            }
+
+            ServiceInvoker serviceInvoker = new ServiceInvoker(kernel, objectName);
+            serviceInvoker.start();
+            registry.put(objectName, serviceInvoker);
+        }
+    }
+
+    private void unregister(ObjectName objectName) {
+        synchronized (this) {
+            ServiceInvoker serviceInvoker = (ServiceInvoker) registry.remove(objectName);
+            if (serviceInvoker != null) {
+                serviceInvoker.stop();
+            }
+        }
+    }
+
+    private class ServiceRegistrationListener extends LifecycleAdapter {
+        public void running(ObjectName objectName) {
+            try {
+                register(objectName);
+            } catch (Exception e) {
+                log.info("Unable to create service invoker for " + objectName, e);
+            }
+        }
+
+        public void stopped(ObjectName objectName) {
+            unregister(objectName);
+        }
+
+        public void unloaded(ObjectName objectName) {
+            unregister(objectName);
+        }
+    }
+}
diff --git a/kernel/src/java/org/gbean/repository/FileSystemRepository.java b/kernel/src/java/org/gbean/repository/FileSystemRepository.java
new file mode 100644
index 0000000..7ea0d50
--- /dev/null
+++ b/kernel/src/java/org/gbean/repository/FileSystemRepository.java
@@ -0,0 +1,60 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.repository;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class FileSystemRepository implements Repository {
+    private File root;
+
+    public FileSystemRepository() {
+    }
+
+    public FileSystemRepository(File root) {
+        this.root = root;
+    }
+
+    public File getRoot() {
+        return root;
+    }
+
+    public void setRoot(File root) {
+        this.root = root;
+    }
+
+    public boolean containsResource(URI uri) {
+        uri = root.toURI().resolve(uri);
+        File file = new File(uri);
+        return file.canRead();
+    }
+
+    public URL getResource(URI uri) {
+        uri = root.toURI().resolve(uri);
+        File file = new File(uri);
+        try {
+            return file.toURL();
+        } catch (MalformedURLException e) {
+            throw new IllegalArgumentException("Malformed resource " + uri);
+        }
+    }
+}
diff --git a/kernel/src/java/org/gbean/repository/Repository.java b/kernel/src/java/org/gbean/repository/Repository.java
new file mode 100644
index 0000000..ea0bff9
--- /dev/null
+++ b/kernel/src/java/org/gbean/repository/Repository.java
@@ -0,0 +1,29 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.repository;
+
+import java.net.URI;
+import java.net.URL;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public interface Repository {
+    boolean containsResource(URI uri);
+
+    URL getResource(URI uri);
+}
diff --git a/kernel/src/java/org/gbean/service/AbstractServiceFactory.java b/kernel/src/java/org/gbean/service/AbstractServiceFactory.java
new file mode 100644
index 0000000..12d91fb
--- /dev/null
+++ b/kernel/src/java/org/gbean/service/AbstractServiceFactory.java
@@ -0,0 +1,72 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.service;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.Collections;
+import java.util.HashMap;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public abstract class AbstractServiceFactory implements ServiceFactory {
+    private boolean enabled = true;
+    private Map dependencies = new HashMap();
+
+    public Map getDependencies() {
+        return dependencies;
+    }
+
+    public void addDependency(String name, Set patterns) {
+        dependencies.put(name, patterns);
+    }
+
+    public void destroyService(ServiceContext serviceContext, Object service) {
+    }
+
+    public Set getPropertyNames() {
+        return Collections.EMPTY_SET;
+    }
+
+    public Map getProperties() {
+        return Collections.EMPTY_MAP;
+    }
+
+    public void setProperties(Map properties) {
+        assert properties != null;
+        if (properties.size() > 0) {
+            throw new IllegalArgumentException("This service factory does not have any properties");
+        }
+    }
+
+    public Object getProperty(String name) {
+        throw new IllegalArgumentException("This service factory does not have any properties");
+    }
+
+    public void setProperty(String name, Object value) {
+        throw new IllegalArgumentException("This service factory does not have any properties");
+    }
+
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+}
diff --git a/kernel/src/java/org/gbean/service/ConfigurableServiceFactory.java b/kernel/src/java/org/gbean/service/ConfigurableServiceFactory.java
new file mode 100644
index 0000000..cdba60b
--- /dev/null
+++ b/kernel/src/java/org/gbean/service/ConfigurableServiceFactory.java
@@ -0,0 +1,30 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.service;
+
+import java.util.Set;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public interface ConfigurableServiceFactory extends ServiceFactory {
+    Set getPropertyNames();
+
+    Object getProperty(String name);
+
+    void setProperty(String name, Object value);
+}
diff --git a/kernel/src/java/org/gbean/service/DynamicService.java b/kernel/src/java/org/gbean/service/DynamicService.java
new file mode 100644
index 0000000..270858b
--- /dev/null
+++ b/kernel/src/java/org/gbean/service/DynamicService.java
@@ -0,0 +1,25 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.service;
+
+public interface DynamicService {
+    Object getAttribute(String s) throws Exception;
+
+    void setAttribute(String s, Object o) throws Exception;
+
+    Object invoke(String s, Object[] objects, String[] strings) throws Exception;
+}
\ No newline at end of file
diff --git a/kernel/src/java/org/gbean/service/ServiceContext.java b/kernel/src/java/org/gbean/service/ServiceContext.java
new file mode 100644
index 0000000..77b0201
--- /dev/null
+++ b/kernel/src/java/org/gbean/service/ServiceContext.java
@@ -0,0 +1,60 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.service;
+
+import org.gbean.kernel.Kernel;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public interface ServiceContext {
+    /**
+     * Gets the object name under which this bean is registered
+     * @return the object name of this bean
+     */
+    String getObjectName();
+
+    /**
+     * Gets the kernel in which this bean is registered
+     * @return the kernel in which this bean is register
+     */
+    Kernel getKernel();
+
+    /**
+     * Gets the class loader used to construct this bean
+     * @return thhe class loader in which this bean was constructed
+     */
+    ClassLoader getClassLoader();
+
+    /**
+     * Gets the state of this component as an int.
+     * The int return is required by the JSR77 specification.
+     *
+     * @return the current state of this component
+     */
+    int getState();
+
+    /**
+     * Attempt to bring the component into the fully stopped state. If an exception occurs while
+     * stopping the component, the component is automaticaly failed.
+     * <p/>
+     * There is no guarantee that the service will be stopped when the method returns.
+     *
+     * @throws Exception if a problem occurs while stopping the component
+     */
+    void stop() throws Exception;
+}
diff --git a/kernel/src/java/org/gbean/service/ServiceFactory.java b/kernel/src/java/org/gbean/service/ServiceFactory.java
new file mode 100644
index 0000000..98b50f4
--- /dev/null
+++ b/kernel/src/java/org/gbean/service/ServiceFactory.java
@@ -0,0 +1,37 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.service;
+
+import java.util.Set;
+import java.util.Map;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public interface ServiceFactory {
+    Map getDependencies();
+
+    void addDependency(String name, Set patterns);
+
+    Object createService(ServiceContext serviceContext) throws Exception;
+
+    void destroyService(ServiceContext serviceContext, Object service);
+
+    boolean isEnabled();
+
+    void setEnabled(boolean enabled);
+}
diff --git a/kernel/src/java/org/gbean/spring/AbstractSpringVisitor.java b/kernel/src/java/org/gbean/spring/AbstractSpringVisitor.java
new file mode 100644
index 0000000..e69657d
--- /dev/null
+++ b/kernel/src/java/org/gbean/spring/AbstractSpringVisitor.java
@@ -0,0 +1,134 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.spring;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.MutablePropertyValues;
+import org.springframework.beans.PropertyValue;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.config.ConstructorArgumentValues;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public abstract class AbstractSpringVisitor implements SpringVisitor {
+    public void visitBeanFactory(ConfigurableListableBeanFactory beanRegistry, Object data) throws BeansException {
+        String[] beanNames = beanRegistry.getBeanDefinitionNames();
+        for (int i = 0; i < beanNames.length; i++) {
+            String beanName = beanNames[i];
+            visitBeanDefinition(beanName, beanRegistry.getBeanDefinition(beanName), data);
+        }
+    }
+
+    public void visitBeanDefinitionHolder(BeanDefinitionHolder beanDefinitionHolder, Object data) throws BeansException {
+        visitBeanDefinition(beanDefinitionHolder.getBeanName(), beanDefinitionHolder.getBeanDefinition(), data);
+    }
+
+    public void visitBeanDefinition(String beanName, BeanDefinition beanDefinition, Object data) throws BeansException {
+        visitBeanDefinition(beanDefinition, data);
+    }
+
+    public void visitBeanDefinition(BeanDefinition beanDefinition, Object data) throws BeansException {
+        visitConstructorArgumentValues(beanDefinition.getConstructorArgumentValues(), data);
+        visitMutablePropertyValues(beanDefinition.getPropertyValues(), data);
+    }
+
+    public void visitMutablePropertyValues(MutablePropertyValues propertyValues, Object data) throws BeansException {
+        PropertyValue[] values = propertyValues.getPropertyValues();
+        for (int i = 0; i < values.length; i++) {
+            visitPropertyValue(values[i], data);
+        }
+    }
+
+    public void visitConstructorArgumentValues(ConstructorArgumentValues constructorArgumentValues, Object data) throws BeansException {
+        Map indexedArgumentValues = constructorArgumentValues.getIndexedArgumentValues();
+        for (Iterator iterator = indexedArgumentValues.values().iterator(); iterator.hasNext();) {
+            visitConstructorArgumentValue((ConstructorArgumentValues.ValueHolder) iterator.next(), data);
+        }
+        List genericArgumentValues = constructorArgumentValues.getGenericArgumentValues();
+        for (Iterator iterator = genericArgumentValues.iterator(); iterator.hasNext();) {
+            visitConstructorArgumentValue((ConstructorArgumentValues.ValueHolder) iterator.next(), data);
+        }
+    }
+
+    public void visitConstructorArgumentValue(ConstructorArgumentValues.ValueHolder valueHolder, Object data) throws BeansException {
+        visitNext(valueHolder.getValue(), data);
+    }
+
+    public void visitPropertyValue(PropertyValue propertyValue, Object data) throws BeansException {
+        visitNext(propertyValue.getValue(), data);
+    }
+
+    public void visitRuntimeBeanReference(RuntimeBeanReference beanReference, Object data) throws BeansException {
+    }
+
+    public void visitCollection(Collection collection, Object data)  throws BeansException {
+        for (Iterator iterator = collection.iterator(); iterator.hasNext();) {
+            visitNext(iterator.next(), data);
+        }
+    }
+
+    public void visitMap(Map map, Object data)  throws BeansException {
+        for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) {
+            Map.Entry entry = (Map.Entry) iterator.next();
+            visitNext(entry.getKey(), data);
+            visitNext(entry.getValue(), data);
+        }
+    }
+
+    public void visitObject(Object value, Object data)  throws BeansException {
+    }
+
+    protected void visitNext(Object value, Object data) throws BeansException {
+        if (value == null) {
+            return;
+        }
+
+        if (value instanceof ConfigurableListableBeanFactory) {
+            visitBeanFactory((ConfigurableListableBeanFactory) value, data);
+        } else if (value instanceof BeanDefinitionHolder) {
+            visitBeanDefinitionHolder((BeanDefinitionHolder) value, data);
+        } else if (value instanceof BeanDefinition) {
+            visitBeanDefinition((BeanDefinition) value, data);
+        } else if (value instanceof ConstructorArgumentValues) {
+            visitConstructorArgumentValues((ConstructorArgumentValues) value, data);
+        } else if (value instanceof ConstructorArgumentValues.ValueHolder) {
+            visitConstructorArgumentValue((ConstructorArgumentValues.ValueHolder) value, data);
+        } else if (value instanceof MutablePropertyValues) {
+            visitMutablePropertyValues((MutablePropertyValues) value, data);
+        } else if (value instanceof PropertyValue) {
+            visitPropertyValue((PropertyValue) value, data);
+        } else if (value instanceof RuntimeBeanReference) {
+            visitRuntimeBeanReference((RuntimeBeanReference) value, data);
+        } else if (value instanceof Map) {
+            visitMap((Map) value, data);
+        } else if (value instanceof Collection) {
+            visitCollection((Collection) value, data);
+        } else {
+            visitObject(value, data);
+        }
+    }
+
+}
diff --git a/kernel/src/java/org/gbean/spring/ClassLoaderReference.java b/kernel/src/java/org/gbean/spring/ClassLoaderReference.java
new file mode 100644
index 0000000..6a67e44
--- /dev/null
+++ b/kernel/src/java/org/gbean/spring/ClassLoaderReference.java
@@ -0,0 +1,51 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.gbean.spring;
+
+import java.io.Serializable;
+
+import org.gbean.service.ServiceContext;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+
+/**
+ * @version $Rev: 71492 $ $Date: 2004-11-14 21:31:50 -0800 (Sun, 14 Nov 2004) $
+ */
+public class ClassLoaderReference implements FactoryBean, Serializable {
+    public static BeanDefinitionHolder createBeanDefinition() {
+        RootBeanDefinition beanDefinition = new RootBeanDefinition(ClassLoaderReference.class, 0);
+        return new BeanDefinitionHolder(beanDefinition, ClassLoaderReference.class.getName());
+    }
+
+    public final Class getObjectType() {
+        return ClassLoader.class;
+    }
+
+    public synchronized final Object getObject() {
+        ServiceContext serviceContext = ServiceContextThreadLocal.get();
+        if (serviceContext == null) {
+            throw new IllegalStateException("Service context has not been set");
+        }
+        return serviceContext.getClassLoader();
+    }
+
+    public boolean isSingleton() {
+        return true;
+    }
+}
diff --git a/kernel/src/java/org/gbean/spring/DefaultProperty.java b/kernel/src/java/org/gbean/spring/DefaultProperty.java
new file mode 100644
index 0000000..a620d76
--- /dev/null
+++ b/kernel/src/java/org/gbean/spring/DefaultProperty.java
@@ -0,0 +1,63 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.spring;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class DefaultProperty {
+    private String name;
+    private Class type;
+    private Object value;
+
+    public DefaultProperty() {
+    }
+
+    public DefaultProperty(String name, Class type, Object value) {
+        this.name = name;
+        this.type = type;
+        this.value = value;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Class getType() {
+        return type;
+    }
+
+    public void setType(Class type) {
+        this.type = type;
+    }
+
+    public Object getValue() {
+        return value;
+    }
+
+    public void setValue(Object value) {
+        this.value = value;
+    }
+
+    public String toString() {
+        return "[" + name + ", " + type + ", " + value + "]";
+    }
+}
diff --git a/kernel/src/java/org/gbean/spring/DependencyProvider.java b/kernel/src/java/org/gbean/spring/DependencyProvider.java
new file mode 100644
index 0000000..4b32409
--- /dev/null
+++ b/kernel/src/java/org/gbean/spring/DependencyProvider.java
@@ -0,0 +1,26 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.spring;
+
+import java.util.Map;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public interface DependencyProvider {
+    Map getDependencies();
+}
diff --git a/kernel/src/java/org/gbean/spring/FatalStartupError.java b/kernel/src/java/org/gbean/spring/FatalStartupError.java
new file mode 100644
index 0000000..d65c11e
--- /dev/null
+++ b/kernel/src/java/org/gbean/spring/FatalStartupError.java
@@ -0,0 +1,47 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.spring;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class FatalStartupError extends Error {
+    private final int exitCode;
+    private static final int DEFAULT_EXIT_CODE = 3;
+
+    public FatalStartupError(String message) {
+        this(message, DEFAULT_EXIT_CODE);
+    }
+
+    public FatalStartupError(String message, int exitCode) {
+        super(message);
+        this.exitCode = exitCode;
+    }
+
+    public FatalStartupError(String message, Throwable cause) {
+        this(message, DEFAULT_EXIT_CODE, cause);
+    }
+
+    public FatalStartupError(String message, int exitCode, Throwable cause) {
+        super(message, cause);
+        this.exitCode = exitCode;
+    }
+
+    public int getExitCode() {
+        return exitCode;
+    }
+}
diff --git a/kernel/src/java/org/gbean/spring/KernelReference.java b/kernel/src/java/org/gbean/spring/KernelReference.java
new file mode 100644
index 0000000..93e846c
--- /dev/null
+++ b/kernel/src/java/org/gbean/spring/KernelReference.java
@@ -0,0 +1,52 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.gbean.spring;
+
+import java.io.Serializable;
+
+import org.gbean.kernel.Kernel;
+import org.gbean.service.ServiceContext;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+
+/**
+ * @version $Rev: 71492 $ $Date: 2004-11-14 21:31:50 -0800 (Sun, 14 Nov 2004) $
+ */
+public class KernelReference implements FactoryBean, Serializable {
+    public static BeanDefinitionHolder createBeanDefinition() {
+        RootBeanDefinition beanDefinition = new RootBeanDefinition(KernelReference.class, 0);
+        return new BeanDefinitionHolder(beanDefinition, KernelReference.class.getName());
+    }
+
+    public final Class getObjectType() {
+        return Kernel.class;
+    }
+
+    public synchronized final Object getObject() {
+        ServiceContext serviceContext = ServiceContextThreadLocal.get();
+        if (serviceContext == null) {
+            throw new IllegalStateException("Service context has not been set");
+        }
+        return serviceContext.getKernel();
+    }
+
+    public boolean isSingleton() {
+        return true;
+    }
+}
diff --git a/kernel/src/java/org/gbean/spring/LifecycleDetector.java b/kernel/src/java/org/gbean/spring/LifecycleDetector.java
new file mode 100644
index 0000000..8dce222
--- /dev/null
+++ b/kernel/src/java/org/gbean/spring/LifecycleDetector.java
@@ -0,0 +1,104 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.spring;
+
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class LifecycleDetector implements BeanFactoryPostProcessor {
+    private final Map lifecycleMap = new LinkedHashMap();
+
+    public List getLifecycleInterfaces() {
+        List lifecycleInterfaces = new LinkedList();
+        for (Iterator iterator = lifecycleMap.entrySet().iterator(); iterator.hasNext();) {
+            Map.Entry entry = (Map.Entry) iterator.next();
+            Class type = (Class) entry.getKey();
+            LifecycleMethods lifecycleMethods = (LifecycleMethods) entry.getValue();
+            lifecycleInterfaces.add(new LifecycleInfo(type, lifecycleMethods.initMethodName, lifecycleMethods.destroyMethodName));
+        }
+        return lifecycleInterfaces;
+    }
+
+    public void setLifecycleInterfaces(List lifecycleInterfaces) {
+        lifecycleMap.clear();
+        for (Iterator iterator = lifecycleInterfaces.iterator(); iterator.hasNext();) {
+            addLifecycleInterface((LifecycleInfo) iterator.next());
+        }
+    }
+
+    public void addLifecycleInterface(Class type, String initMethodName, String destroyMethodName) {
+        lifecycleMap.put(type, new LifecycleMethods(initMethodName, destroyMethodName));
+    }
+
+    public void addLifecycleInterface(LifecycleInfo lifecycleInfo) {
+        lifecycleMap.put(lifecycleInfo.getType(),
+                new LifecycleMethods(lifecycleInfo.getInitMethodName(), lifecycleInfo.getDestroyMethodName()));
+    }
+
+    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+        SpringVisitor visitor = new AbstractSpringVisitor() {
+            public void visitBeanDefinition(BeanDefinition beanDefinition, Object data) throws BeansException {
+                super.visitBeanDefinition(beanDefinition, data);
+
+                if (!(beanDefinition instanceof RootBeanDefinition)) {
+                    return;
+                }
+                RootBeanDefinition rootBeanDefinition = ((RootBeanDefinition) beanDefinition);
+                Class beanType = rootBeanDefinition.getBeanClass();
+                for (Iterator iterator = lifecycleMap.entrySet().iterator(); iterator.hasNext();) {
+                    Map.Entry entry = (Map.Entry) iterator.next();
+                    Class lifecycleInterface = (Class) entry.getKey();
+                    LifecycleMethods value = (LifecycleMethods) entry.getValue();
+                    if (lifecycleInterface.isAssignableFrom(beanType)) {
+                        if (rootBeanDefinition.getInitMethodName() == null) {
+                            rootBeanDefinition.setInitMethodName(value.initMethodName);
+                        }
+                        if (rootBeanDefinition.getDestroyMethodName() == null) {
+                            rootBeanDefinition.setDestroyMethodName(value.destroyMethodName);
+                        }
+                        if (rootBeanDefinition.getInitMethodName() != null && rootBeanDefinition.getDestroyMethodName() != null) {
+                            return;
+                        }
+                    }
+                }
+            }
+        };
+        visitor.visitBeanFactory(beanFactory, null);
+    }
+
+    private static class LifecycleMethods {
+        private String initMethodName;
+        private String destroyMethodName;
+
+        public LifecycleMethods(String initMethodName, String destroyMethodName) {
+            this.initMethodName = initMethodName;
+            this.destroyMethodName = destroyMethodName;
+        }
+    }
+}
diff --git a/kernel/src/java/org/gbean/spring/LifecycleInfo.java b/kernel/src/java/org/gbean/spring/LifecycleInfo.java
new file mode 100644
index 0000000..e80eaf2
--- /dev/null
+++ b/kernel/src/java/org/gbean/spring/LifecycleInfo.java
@@ -0,0 +1,59 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.spring;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class LifecycleInfo {
+    private Class type;
+    private String initMethodName;
+    private String destroyMethodName;
+
+    public LifecycleInfo() {
+    }
+
+    public LifecycleInfo(Class type, String initMethodName, String destroyMethodName) {
+        this.type = type;
+        this.initMethodName = initMethodName;
+        this.destroyMethodName = destroyMethodName;
+    }
+
+    public Class getType() {
+        return type;
+    }
+
+    public void setType(Class type) {
+        this.type = type;
+    }
+
+    public String getInitMethodName() {
+        return initMethodName;
+    }
+
+    public void setInitMethodName(String initMethodName) {
+        this.initMethodName = initMethodName;
+    }
+
+    public String getDestroyMethodName() {
+        return destroyMethodName;
+    }
+
+    public void setDestroyMethodName(String destroyMethodName) {
+        this.destroyMethodName = destroyMethodName;
+    }
+}
diff --git a/kernel/src/java/org/gbean/spring/LiveHashSetReference.java b/kernel/src/java/org/gbean/spring/LiveHashSetReference.java
new file mode 100644
index 0000000..ba48d49
--- /dev/null
+++ b/kernel/src/java/org/gbean/spring/LiveHashSetReference.java
@@ -0,0 +1,91 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.gbean.spring;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.Set;
+import javax.management.ObjectName;
+
+import org.gbean.beans.LiveHashSet;
+import org.gbean.service.ServiceContext;
+import org.springframework.beans.MutablePropertyValues;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+
+/**
+ * @version $Rev: 71492 $ $Date: 2004-11-14 21:31:50 -0800 (Sun, 14 Nov 2004) $
+ */
+public class LiveHashSetReference implements FactoryBean, Serializable {
+    public static BeanDefinitionHolder createBeanDefinition(String name, Set patterns) {
+        RootBeanDefinition beanDefinition = new RootBeanDefinition(LiveHashSetReference.class, 0);
+        MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
+        propertyValues.addPropertyValue("name", name);
+        propertyValues.addPropertyValue("patterns", patterns);
+        return new BeanDefinitionHolder(beanDefinition, LiveHashSetReference.class.getName());
+    }
+
+    /**
+     * Name of this reference.
+     */
+    private String name;
+
+    /**
+     * The target objectName patterns to watch for a connection.
+     */
+    private Set patterns;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Set getPatterns() {
+        return patterns;
+    }
+
+    public void setPatterns(Set patterns) {
+        this.patterns = patterns;
+    }
+
+    public void setPattern(ObjectName pattern) {
+        this.patterns = Collections.singleton(pattern);
+    }
+
+    public final Class getObjectType() {
+        return Set.class;
+    }
+
+    public synchronized final Object getObject() {
+        ServiceContext serviceContext = ServiceContextThreadLocal.get();
+        if (serviceContext == null) {
+            throw new IllegalStateException("Service context has not been set");
+        }
+        LiveHashSet liveHashSet = new LiveHashSet(serviceContext.getKernel(), name, patterns);
+        liveHashSet.start();
+        return liveHashSet;
+    }
+
+    public boolean isSingleton() {
+        return true;
+    }
+}
diff --git a/kernel/src/java/org/gbean/spring/LiveProxyHashSetReference.java b/kernel/src/java/org/gbean/spring/LiveProxyHashSetReference.java
new file mode 100644
index 0000000..9c03e68
--- /dev/null
+++ b/kernel/src/java/org/gbean/spring/LiveProxyHashSetReference.java
@@ -0,0 +1,107 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.gbean.spring;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.Set;
+import javax.management.ObjectName;
+
+import org.gbean.beans.LiveProxyHashSet;
+import org.gbean.kernel.ClassLoading;
+import org.gbean.service.ServiceContext;
+import org.springframework.beans.MutablePropertyValues;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+
+/**
+ * @version $Rev: 71492 $ $Date: 2004-11-14 21:31:50 -0800 (Sun, 14 Nov 2004) $
+ */
+public class LiveProxyHashSetReference implements FactoryBean, Serializable {
+    public static BeanDefinitionHolder createBeanDefinition(String name, Set patterns, String type) {
+        RootBeanDefinition beanDefinition = new RootBeanDefinition(LiveHashSetReference.class, 0);
+        MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
+        propertyValues.addPropertyValue("name", name);
+        propertyValues.addPropertyValue("patterns", patterns);
+        propertyValues.addPropertyValue("type", type);
+        return new BeanDefinitionHolder(beanDefinition, LiveHashSetReference.class.getName());
+    }
+
+    /**
+     * Name of this reference.
+     */
+    private String name;
+
+    /**
+     * Proxy type which is injected into the service.
+     */
+    private String type;
+
+    /**
+     * The target objectName patterns to watch for a connection.
+     */
+    private Set patterns;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public Set getPatterns() {
+        return patterns;
+    }
+
+    public void setPatterns(Set patterns) {
+        this.patterns = patterns;
+    }
+
+    public void setPattern(ObjectName pattern) {
+        this.patterns = Collections.singleton(pattern);
+    }
+
+    public final Class getObjectType() {
+        return Set.class;
+    }
+
+    public synchronized final Object getObject() throws ClassNotFoundException {
+        ServiceContext serviceContext = ServiceContextThreadLocal.get();
+        if (serviceContext == null) {
+            throw new IllegalStateException("Service context has not been set");
+        }
+        Class proxyType = ClassLoading.loadClass(type, serviceContext.getClassLoader());
+        LiveProxyHashSet liveProxyHashSet = new LiveProxyHashSet(serviceContext.getKernel(), name, patterns, proxyType);
+        liveProxyHashSet.start();
+        return liveProxyHashSet;
+    }
+
+    public boolean isSingleton() {
+        return true;
+    }
+}
diff --git a/kernel/src/java/org/gbean/spring/NamedConstructorArgs.java b/kernel/src/java/org/gbean/spring/NamedConstructorArgs.java
new file mode 100644
index 0000000..38436b4
--- /dev/null
+++ b/kernel/src/java/org/gbean/spring/NamedConstructorArgs.java
@@ -0,0 +1,290 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.spring;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.LinkedList;
+
+import org.gbean.metadata.ClassMetadata;
+import org.gbean.metadata.ConstructorMetadata;
+import org.gbean.metadata.MetadataManager;
+import org.gbean.metadata.ParameterMetadata;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.MutablePropertyValues;
+import org.springframework.beans.PropertyValue;
+import org.springframework.beans.FatalBeanException;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.config.ConstructorArgumentValues;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.beans.factory.FactoryBean;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class NamedConstructorArgs implements BeanFactoryPostProcessor {
+    private MetadataManager metadataManager;
+    private Map defaultValues = new HashMap();
+
+    public NamedConstructorArgs() {
+    }
+
+    public NamedConstructorArgs(MetadataManager metadataManager) {
+        this.metadataManager = metadataManager;
+    }
+
+    public MetadataManager getMetadataManager() {
+        return metadataManager;
+    }
+
+    public void setMetadataManager(MetadataManager metadataManager) {
+        this.metadataManager = metadataManager;
+    }
+
+    public List getDefaultValues() {
+        List values = new LinkedList();
+        for (Iterator iterator = defaultValues.entrySet().iterator(); iterator.hasNext();) {
+            Map.Entry entry = (Map.Entry) iterator.next();
+            PropertyKey key = (PropertyKey) entry.getKey();
+            Object value = entry.getValue();
+            values.add(new DefaultProperty(key.name, key.type, value));
+        }
+        return values;
+    }
+
+    public void setDefaultValues(List defaultValues) {
+        this.defaultValues.clear();
+        for (Iterator iterator = defaultValues.iterator(); iterator.hasNext();) {
+            addDefaultValue((DefaultProperty) iterator.next());
+        }
+    }
+
+    public void addDefaultValue(String name, Class type, Object value) {
+        defaultValues.put(new PropertyKey(name, type), value);
+    }
+
+    private void addDefaultValue(DefaultProperty defaultProperty) {
+        defaultValues.put(new PropertyKey(defaultProperty.getName(), defaultProperty.getType()), defaultProperty.getValue());
+    }
+
+    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+        SpringVisitor visitor = new AbstractSpringVisitor() {
+            public void visitBeanDefinition(BeanDefinition beanDefinition, Object data) throws BeansException {
+                super.visitBeanDefinition(beanDefinition, data);
+
+                if (!(beanDefinition instanceof RootBeanDefinition)) {
+                    return;
+                }
+
+                RootBeanDefinition rootBeanDefinition = ((RootBeanDefinition) beanDefinition);
+                processParameters(rootBeanDefinition);
+
+            }
+        };
+        visitor.visitBeanFactory(beanFactory, null);
+    }
+
+    private void processParameters(RootBeanDefinition rootBeanDefinition) throws BeansException {
+        ConstructorArgumentValues constructorArgumentValues = rootBeanDefinition.getConstructorArgumentValues();
+
+        // if this bean already has constructor arguments defined, don't mess with them
+        if (constructorArgumentValues.getArgumentCount() > 0) {
+            return;
+        }
+
+        // try to get a list of constructor arg names to use
+        ConstructorMetadata constructorMetadata = getConstructor(rootBeanDefinition);
+        if (constructorMetadata == null) {
+            return;
+        }
+
+        // remove each named property and add an indexed constructor arg
+        MutablePropertyValues propertyValues = rootBeanDefinition.getPropertyValues();
+        List parameters = constructorMetadata.getParameters();
+        for (ListIterator iterator = parameters.listIterator(); iterator.hasNext();) {
+            ParameterMetadata parameterMetadata = (ParameterMetadata) iterator.next();
+            String name = (String) parameterMetadata.get("name");
+
+            Class parameterType = parameterMetadata.getType();
+            PropertyValue propertyValue = propertyValues.getPropertyValue(name);
+            if (propertyValue != null) {
+                propertyValues.removePropertyValue(name);
+                constructorArgumentValues.addIndexedArgumentValue(iterator.previousIndex(), propertyValue.getValue(), parameterType.getName());
+            } else {
+                Object defaultValue = defaultValues.get(new PropertyKey(name, parameterType));
+                if (defaultValue == null) {
+                    defaultValue = DEFAULT_VALUE.get(parameterType);
+                }
+                if (defaultValue instanceof FactoryBean) {
+                    try {
+                        defaultValue = ((FactoryBean)defaultValue).getObject();
+                    } catch (Exception e) {
+                        throw new FatalBeanException("Unable to get object value from bean factory", e);
+                    }
+                }
+                constructorArgumentValues.addIndexedArgumentValue(iterator.previousIndex(), defaultValue, parameterType.getName());
+            }
+        }
+
+        // todo set any usable default values on the bean definition
+    }
+
+    private ConstructorMetadata getConstructor(RootBeanDefinition rootBeanDefinition) {
+        Class beanType = rootBeanDefinition.getBeanClass();
+
+        // try to get the class metadata
+        ClassMetadata classMetadata = metadataManager.getClassMetadata(beanType);
+
+        // get a set containing the names of the defined properties
+        Set propertyNames = new HashSet();
+        PropertyValue[] values = rootBeanDefinition.getPropertyValues().getPropertyValues();
+        for (int i = 0; i < values.length; i++) {
+            propertyNames.add(values[i].getName());
+        }
+
+        // get the constructors sorted by longest arg length first
+        List constructors = new ArrayList(classMetadata.getConstructors());
+        Collections.sort(constructors, new ArgLengthComparator());
+
+        // try to find a constructor for which we have all of the properties defined
+        for (Iterator iterator = constructors.iterator(); iterator.hasNext();) {
+            ConstructorMetadata constructorMetadata = (ConstructorMetadata) iterator.next();
+            if (isUsableConstructor(constructorMetadata, propertyNames)) {
+                return constructorMetadata;
+            }
+        }
+        return null;
+    }
+
+    private boolean isUsableConstructor(ConstructorMetadata constructorMetadata, Set propertyNames) {
+        if (constructorMetadata.getProperties().containsKey("always-use")) {
+            return true;
+        }
+
+        LinkedHashMap constructorArgs = getConstructorArgs(constructorMetadata);
+        if (constructorArgs == null) {
+            return false;
+        }
+
+        for (Iterator argIterator = constructorArgs.entrySet().iterator(); argIterator.hasNext();) {
+            Map.Entry entry = (Map.Entry) argIterator.next();
+            String parameterName = (String) entry.getKey();
+            Class parameterType = (Class) entry.getValue();
+            // can we satify this property using a definde proeprty or default property
+            if (!propertyNames.contains(parameterName) && !defaultValues.containsKey(new PropertyKey(parameterName, parameterType))) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private LinkedHashMap getConstructorArgs(ConstructorMetadata constructor) {
+        LinkedHashMap constructorArgs = new LinkedHashMap();
+
+        List parameterMetadata = constructor.getParameters();
+        for (Iterator iterator = parameterMetadata.iterator(); iterator.hasNext();) {
+            ParameterMetadata parameter = (ParameterMetadata) iterator.next();
+            String name = (String) parameter.get("name");
+            if (name == null) {
+                return null;
+            }
+            constructorArgs.put(name, parameter.getType());
+        }
+        return constructorArgs;
+    }
+
+    private static class ArgLengthComparator implements Comparator {
+        public int compare(Object o1, Object o2) {
+            ConstructorMetadata constructor1 = (ConstructorMetadata) o1;
+            ConstructorMetadata constructor2 = (ConstructorMetadata) o2;
+            return constructor2.getParameters().size() - constructor1.getParameters().size();
+        }
+    }
+
+    private static class PropertyKey {
+        private String name;
+        private Class type;
+
+        public PropertyKey(String name, Class type) {
+            this.name = name;
+            this.type = type;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public Class getType() {
+            return type;
+        }
+
+        public void setType(Class type) {
+            this.type = type;
+        }
+
+        public boolean equals(Object object) {
+            if (!(object instanceof PropertyKey)) {
+                return false;
+            }
+
+            PropertyKey defaultProperty = (PropertyKey) object;
+            return name.equals(defaultProperty.name) && type.equals(type);
+        }
+
+        public int hashCode() {
+            int result = 17;
+            result = 37 * result + name.hashCode();
+            result = 37 * result + type.hashCode();
+            return result;
+        }
+
+        public String toString() {
+            return "[" + name + " " + type + "]";
+        }
+    }
+
+    private static final Map DEFAULT_VALUE;
+    static {
+        Map temp = new HashMap();
+        temp.put(Boolean.TYPE, Boolean.FALSE);
+        temp.put(Byte.TYPE, new Byte((byte) 0));
+        temp.put(Character.TYPE, new Character((char) 0));
+        temp.put(Short.TYPE, new Short((short) 0));
+        temp.put(Integer.TYPE, new Integer(0));
+        temp.put(Long.TYPE, new Long(0));
+        temp.put(Float.TYPE, new Float(0));
+        temp.put(Double.TYPE, new Double(0));
+
+        DEFAULT_VALUE = Collections.unmodifiableMap(temp);
+    }
+}
diff --git a/kernel/src/java/org/gbean/spring/ObjectNameBuilder.java b/kernel/src/java/org/gbean/spring/ObjectNameBuilder.java
new file mode 100644
index 0000000..5a18b55
--- /dev/null
+++ b/kernel/src/java/org/gbean/spring/ObjectNameBuilder.java
@@ -0,0 +1,102 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.spring;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import javax.management.ObjectName;
+
+import org.gbean.kernel.ServiceName;
+import org.gbean.metadata.ClassMetadata;
+import org.gbean.metadata.MetadataManager;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.MutablePropertyValues;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class ObjectNameBuilder implements BeanFactoryPostProcessor {
+    private static final String OBJECT_NAME_PROPERTY = "gbean-object-name";
+    private final MetadataManager metadataManager;
+    private final String domainName;
+    private final String serverName;
+    private final String moduleName;
+    private final Map objectNameMap = new HashMap();
+
+    public ObjectNameBuilder(MetadataManager metadataManager, String domainName, String serverName, String moduleName) {
+        this.metadataManager = metadataManager;
+        this.domainName = domainName;
+        this.serverName = serverName;
+        this.moduleName = moduleName;
+    }
+
+    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+        String[] beanNames = beanFactory.getBeanDefinitionNames();
+        if (beanNames == null) {
+            return;
+        }
+        for (int i = 0; i < beanNames.length; i++) {
+            String beanName = beanNames[i];
+            BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
+            if (beanDefinition instanceof RootBeanDefinition) {
+                ObjectName objectName = buildObjectName(beanName, (RootBeanDefinition) beanDefinition);
+                objectNameMap.put(beanName, objectName);
+            }
+        }
+    }
+
+    private ObjectName buildObjectName(String beanName, RootBeanDefinition beanDefinition) {
+        MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
+        if (propertyValues.contains(OBJECT_NAME_PROPERTY)) {
+            String objectNameString = (String) propertyValues.getPropertyValue(OBJECT_NAME_PROPERTY).getValue();
+            propertyValues.removePropertyValue(OBJECT_NAME_PROPERTY);
+            return ServiceName.createName(objectNameString);
+        } else {
+            ClassMetadata classMetadata = metadataManager.getClassMetadata(beanDefinition.getBeanClass());
+
+            String type = (String) classMetadata.get("j2eeType");
+            return createObjectName(beanName, type);
+        }
+    }
+
+    public Map getObjectNameMap() {
+        return objectNameMap;
+    }
+
+    public ObjectName getObjectName(String beanName) {
+        return (ObjectName) objectNameMap.get(beanName);
+    }
+
+    private ObjectName createObjectName(String name, String type) {
+        Properties props = new Properties();
+        if (type != null) {
+            props.put("j2eeType", type);
+        }
+        props.put("name", name);
+        props.put("J2EEApplication", "null");
+        props.put("J2EEModule", moduleName);
+        props.put("J2EEServer", serverName);
+
+        return ServiceName.createName(domainName, props);
+    }
+
+}
diff --git a/kernel/src/java/org/gbean/spring/ObjectNameReference.java b/kernel/src/java/org/gbean/spring/ObjectNameReference.java
new file mode 100644
index 0000000..fa65008
--- /dev/null
+++ b/kernel/src/java/org/gbean/spring/ObjectNameReference.java
@@ -0,0 +1,53 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.gbean.spring;
+
+import java.io.Serializable;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import org.gbean.service.ServiceContext;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+
+/**
+ * @version $Rev: 71492 $ $Date: 2004-11-14 21:31:50 -0800 (Sun, 14 Nov 2004) $
+ */
+public class ObjectNameReference implements FactoryBean, Serializable {
+    public static BeanDefinitionHolder createBeanDefinition() {
+        RootBeanDefinition beanDefinition = new RootBeanDefinition(ObjectNameReference.class, 0);
+        return new BeanDefinitionHolder(beanDefinition, ObjectNameReference.class.getName());
+    }
+
+    public final Class getObjectType() {
+        return ObjectName.class;
+    }
+
+    public synchronized final Object getObject() throws MalformedObjectNameException {
+        ServiceContext serviceContext = ServiceContextThreadLocal.get();
+        if (serviceContext == null) {
+            throw new IllegalStateException("Service context has not been set");
+        }
+        return new ObjectName(serviceContext.getObjectName());
+    }
+
+    public boolean isSingleton() {
+        return true;
+    }
+}
diff --git a/kernel/src/java/org/gbean/spring/ObjectNameStringReference.java b/kernel/src/java/org/gbean/spring/ObjectNameStringReference.java
new file mode 100644
index 0000000..a692745
--- /dev/null
+++ b/kernel/src/java/org/gbean/spring/ObjectNameStringReference.java
@@ -0,0 +1,51 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.gbean.spring;
+
+import java.io.Serializable;
+
+import org.gbean.service.ServiceContext;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+
+/**
+ * @version $Rev: 71492 $ $Date: 2004-11-14 21:31:50 -0800 (Sun, 14 Nov 2004) $
+ */
+public class ObjectNameStringReference implements FactoryBean, Serializable {
+    public static BeanDefinitionHolder createBeanDefinition() {
+        RootBeanDefinition beanDefinition = new RootBeanDefinition(ObjectNameStringReference.class, 0);
+        return new BeanDefinitionHolder(beanDefinition, ObjectNameStringReference.class.getName());
+    }
+
+    public final Class getObjectType() {
+        return String.class;
+    }
+
+    public synchronized final Object getObject() {
+        ServiceContext serviceContext = ServiceContextThreadLocal.get();
+        if (serviceContext == null) {
+            throw new IllegalStateException("Service context has not been set");
+        }
+        return serviceContext.getObjectName();
+    }
+
+    public boolean isSingleton() {
+        return true;
+    }
+}
diff --git a/kernel/src/java/org/gbean/spring/ServiceContextThreadLocal.java b/kernel/src/java/org/gbean/spring/ServiceContextThreadLocal.java
new file mode 100644
index 0000000..d0d4110
--- /dev/null
+++ b/kernel/src/java/org/gbean/spring/ServiceContextThreadLocal.java
@@ -0,0 +1,37 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.spring;
+
+import org.gbean.service.ServiceContext;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public final class ServiceContextThreadLocal {
+    private ServiceContextThreadLocal() {
+    }
+
+    private static final ThreadLocal THREAD_LOCAL = new ThreadLocal();
+
+    public static ServiceContext get() {
+        return (ServiceContext) THREAD_LOCAL.get();
+    }
+
+    public static void set(ServiceContext serviceContext) {
+        THREAD_LOCAL.set(serviceContext);
+    }
+}
diff --git a/kernel/src/java/org/gbean/spring/SpringBootstrap.java b/kernel/src/java/org/gbean/spring/SpringBootstrap.java
new file mode 100644
index 0000000..e4a1a4a
--- /dev/null
+++ b/kernel/src/java/org/gbean/spring/SpringBootstrap.java
@@ -0,0 +1,161 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.spring;
+
+import java.beans.PropertyEditorManager;
+import java.io.File;
+import java.net.JarURLConnection;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+
+import org.apache.log4j.ConsoleAppender;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.PatternLayout;
+import org.gbean.kernel.Main;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.FatalBeanException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.support.AbstractXmlApplicationContext;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class SpringBootstrap {
+    private static final String GBEAN_BOOTSTRAP_MANIFEST = "GBean-Bootstrap";
+    private static final String BOOTSTRAP_FLAG = "--bootstrap";
+    private static final String DEFAULT_BOOTSTRAP = "META-INF/gbean-bootstrap.xml";
+
+    public static void main(String[] args) {
+        ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
+        Thread.currentThread().setContextClassLoader(SpringBootstrap.class.getClassLoader());
+        try {
+            // lame hard coded log initialization
+            Logger root = Logger.getRootLogger();
+            root.addAppender(new ConsoleAppender(new PatternLayout("%d{ABSOLUTE} %-5p [%c{1}] %m%n")));
+            root.setLevel(Level.INFO);
+
+            // add our property editors into the system
+            List editorSearchPath = new LinkedList(Arrays.asList(PropertyEditorManager.getEditorSearchPath()));
+            editorSearchPath.add("org.gbean.propertyeditor");
+            PropertyEditorManager.setEditorSearchPath((String[]) editorSearchPath.toArray(new String[editorSearchPath.size()]));
+
+            // check if bootstrap configuration was specified on the command line
+            String gbeanBootstrap = null;
+            if (args.length > 1 && BOOTSTRAP_FLAG.equals(args[0])) {
+                gbeanBootstrap = args[1];
+            }
+
+            // Determine the gbean installation directory
+            // guess from the location of the jar
+            File baseDirectory;
+            URL url = SpringBootstrap.class.getClassLoader().getResource("META-INF/startup-jar");
+            if (url != null) {
+                try {
+                    JarURLConnection jarConnection = (JarURLConnection) url.openConnection();
+                    url = jarConnection.getJarFileURL();
+
+                    URI baseURI = new URI(url.toString()).resolve("..");
+                    baseDirectory = new File(baseURI);
+
+                    Manifest manifest;
+                    manifest = jarConnection.getManifest();
+                    Attributes mainAttributes = manifest.getMainAttributes();
+                    if (gbeanBootstrap == null) {
+                        gbeanBootstrap = mainAttributes.getValue(GBEAN_BOOTSTRAP_MANIFEST);
+                    }
+                } catch (Exception e) {
+                    System.err.println("Could not determine gbean installation directory");
+                    e.printStackTrace();
+                    System.exit(9);
+                    return;
+                }
+            } else {
+                String dir = System.getProperty("gbean.base.dir", System.getProperty("user.dir"));
+                baseDirectory = new File(dir);
+            }
+            System.setProperty("gbean.base.dir", baseDirectory.getAbsolutePath());
+
+            if (gbeanBootstrap == null) {
+                gbeanBootstrap = DEFAULT_BOOTSTRAP;
+            }
+
+            // check if bootstrap is a file
+            BeanFactory factory = createBeanFactory(baseDirectory, gbeanBootstrap);
+            Main main = (Main) factory.getBean("Main");
+
+            // start it up
+            try {
+                main.main(args);
+            } catch (FatalStartupError e) {
+                System.err.println(e.getMessage());
+                if (e.getCause() != null) {
+                    e.getCause().printStackTrace();
+                }
+                System.exit(e.getExitCode());
+            } catch (Throwable e) {
+                System.err.println("Unknown error");
+                e.printStackTrace();
+                System.exit(9);
+            }
+        } finally {
+            Thread.currentThread().setContextClassLoader(oldClassLoader);
+        }
+    }
+
+    private static BeanFactory createBeanFactory(File baseDirectory, String gbeanBootstrap) {
+        // check if bootstrap file is on the local file system first
+        URI uri = baseDirectory.toURI().resolve(gbeanBootstrap);
+        File bootstrapFile = new File(uri);
+        if (bootstrapFile.canRead()) {
+            try {
+                return new GBeanXmlApplicationContext(bootstrapFile.toURL().toString());
+            } catch (MalformedURLException e) {
+                throw new FatalBeanException("Error creating url for bootstrap file", e);
+            }
+        }
+
+        // assume it is a classpath resource
+        return new GBeanXmlApplicationContext("classpath:" + gbeanBootstrap);
+    }
+
+    public static class GBeanXmlApplicationContext extends AbstractXmlApplicationContext {
+        private String[] configLocations;
+
+        public GBeanXmlApplicationContext(String configLocation) throws BeansException {
+            this.configLocations = new String[]{configLocation};
+            refresh();
+        }
+
+        public GBeanXmlApplicationContext(String configLocation, ApplicationContext parent) throws BeansException {
+            super(parent);
+            this.configLocations = new String[]{configLocation};
+            refresh();
+        }
+
+        protected String[] getConfigLocations() {
+            return this.configLocations;
+        }
+    }
+}
diff --git a/kernel/src/java/org/gbean/spring/SpringConfiguration.java b/kernel/src/java/org/gbean/spring/SpringConfiguration.java
new file mode 100644
index 0000000..19f75e6
--- /dev/null
+++ b/kernel/src/java/org/gbean/spring/SpringConfiguration.java
@@ -0,0 +1,150 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.spring;
+
+import java.net.URI;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.gbean.configuration.Configuration;
+import org.gbean.configuration.ConfigurationInfo;
+import org.gbean.kernel.Kernel;
+import org.gbean.kernel.runtime.ServiceState;
+import org.gbean.kernel.simple.SimpleLifecycle;
+import org.gbean.service.ServiceFactory;
+import org.gbean.classloader.DestroyableClassLoader;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class SpringConfiguration implements Configuration, SimpleLifecycle {
+    private static final Log log = LogFactory.getLog(SpringConfiguration.class);
+
+    public static ObjectName getConfigurationObjectName(URI configId) throws MalformedObjectNameException {
+        // todo geronimo insists on have the configurations in a domain named geronimo.config
+        return new ObjectName("geronimo.config:name=" + ObjectName.quote(configId.toString()));
+    }
+
+    private final Kernel kernel;
+
+    private final ObjectName objectName;
+
+    private final URI configurationId;
+
+    private final String domain;
+
+    private final String server;
+
+    private final Map serviceFactories;
+
+    private final ClassLoader classLoader;
+
+    public SpringConfiguration(Kernel kernel,
+            ConfigurationInfo configurationInfo,
+            Map serviceFactories,
+            ClassLoader classLoader) throws Exception {
+
+        this.kernel = kernel;
+        this.configurationId = configurationInfo.getConfigurationId();
+        this.objectName = getConfigurationObjectName(configurationId);
+
+        this.domain = configurationInfo.getDomain();
+        this.server = configurationInfo.getServer();
+
+        this.serviceFactories = serviceFactories;
+
+        this.classLoader = classLoader;
+    }
+
+    public ObjectName getObjectName() {
+        return objectName;
+    }
+
+    public URI getConfigurationId() {
+        return configurationId;
+    }
+
+    public URI getParentId() {
+        return null;
+    }
+
+    public String getDomain() {
+        return domain;
+    }
+
+    public String getServer() {
+        return server;
+    }
+
+    public ClassLoader getClassLoader() {
+        return classLoader;
+    }
+
+    // here for compatability with geronimo
+    public ClassLoader getConfigurationClassLoader() {
+        return classLoader;
+    }
+
+    public Set getServiceNames() {
+        return new HashSet(serviceFactories.keySet());
+    }
+
+    public void start() throws Exception {
+        for (Iterator iterator = serviceFactories.entrySet().iterator(); iterator.hasNext();) {
+            Map.Entry entry = (Map.Entry) iterator.next();
+            ObjectName serviceName = (ObjectName) entry.getKey();
+            ServiceFactory serviceFactory = (ServiceFactory) entry.getValue();
+            serviceFactory.addDependency("SpringConfiguration", Collections.singleton(objectName));
+
+            kernel.loadService(serviceName, serviceFactory, classLoader);
+            kernel.startService(serviceName);
+            int serviceState = kernel.getServiceState(serviceName);
+            String msg = serviceName.getCanonicalName() + " - " + ServiceState.fromIndex(serviceState);
+            log.info(msg);
+        }
+
+        log.info("Started configuration " + configurationId);
+    }
+
+    public void stop() {
+        log.info("Stopping configuration " + configurationId);
+
+        // unregister all services
+        for (Iterator i = serviceFactories.keySet().iterator(); i.hasNext();) {
+            ObjectName serviceName = (ObjectName) i.next();
+            try {
+                log.trace("Unload service " + serviceName);
+                kernel.unloadService(serviceName);
+            } catch (Exception e) {
+                // ignore
+                log.warn("Could not unload service " + serviceName, e);
+            }
+        }
+
+        if (classLoader instanceof DestroyableClassLoader) {
+            DestroyableClassLoader destroyableClassLoader = (DestroyableClassLoader) classLoader;
+            destroyableClassLoader.destroy();
+        }
+    }                                        
+}
diff --git a/kernel/src/java/org/gbean/spring/SpringLoader.java b/kernel/src/java/org/gbean/spring/SpringLoader.java
new file mode 100644
index 0000000..8a6e9df
--- /dev/null
+++ b/kernel/src/java/org/gbean/spring/SpringLoader.java
@@ -0,0 +1,293 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.spring;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import javax.management.ObjectName;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.gbean.configuration.ConfigurationInfo;
+import org.gbean.configuration.ConfigurationUtil;
+import org.gbean.configuration.InvalidConfigurationException;
+import org.gbean.kernel.Kernel;
+import org.gbean.kernel.simple.SimpleServiceFactory;
+import org.gbean.loader.Loader;
+import org.gbean.metadata.MetadataManager;
+import org.gbean.service.ServiceFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.Resource;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Text;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class SpringLoader implements Loader {
+    private static final Log log = LogFactory.getLog(SpringLoader.class);
+    private static final String CONFIG_ID_NAME = "configId";
+    private static final String PARENT_ID_NAME = "parentId";
+    private static final String DOMAIN_NAME = "domain";
+    private static final String SERVER_NAME = "server";
+
+    private Kernel kernel;
+    private Collection repositories;
+    private MetadataManager metadataManager;
+    private List beanFactoryPostProcessors;
+    private File baseDir;
+
+    public SpringLoader() {
+    }
+
+    public Kernel getKernel() {
+        return kernel;
+    }
+
+    public void setKernel(Kernel kernel) {
+        this.kernel = kernel;
+    }
+
+    public Collection getRepositories() {
+        return repositories;
+    }
+
+    public void setRepositories(Collection repositories) {
+        this.repositories = repositories;
+    }
+
+    public MetadataManager getMetadataManager() {
+        return metadataManager;
+    }
+
+    public void setMetadataManager(MetadataManager metadataManager) {
+        this.metadataManager = metadataManager;
+    }
+
+    public List getBeanFactoryPostProcessors() {
+        return beanFactoryPostProcessors;
+    }
+
+    public void setBeanFactoryPostProcessors(List beanFactoryPostProcessors) {
+        this.beanFactoryPostProcessors = beanFactoryPostProcessors;
+    }
+
+    public File getBaseDir() {
+        return baseDir;
+    }
+
+    public void setBaseDir(File baseDir) {
+        this.baseDir = baseDir;
+    }
+
+    public ObjectName load(String location) {
+        String resolvedLocation = resolveLocation(location);
+        try {
+            ConfigurationInfo configurationInfo = loadConfigurationInfo(resolvedLocation);
+            final ClassLoader classLoader = ConfigurationUtil.createClassLoader(configurationInfo.getConfigurationId().toString(),
+                    configurationInfo.getDependencies(),
+                    getClass().getClassLoader(),
+                    repositories);
+
+            DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
+            try {
+                Resource resource = new FileSystemResource(new File(resolvedLocation + ".xml"));
+                XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory) {
+                    public ClassLoader getBeanClassLoader() {
+                        return classLoader;
+                    }
+                };
+                reader.loadBeanDefinitions(resource);
+            } catch (BeansException e) {
+                // not a spring file
+                return null;
+            }
+
+            // create object names for all of the beans
+            ObjectNameBuilder objectNameBuilder =  new ObjectNameBuilder(metadataManager,
+                    configurationInfo.getDomain(),
+                    configurationInfo.getServer(),
+                    configurationInfo.getConfigurationId().toString());
+            objectNameBuilder.postProcessBeanFactory(factory);
+
+            Map serviceFactories = new LinkedHashMap();
+            String[] beanDefinitionNames = factory.getBeanDefinitionNames();
+            if (beanDefinitionNames != null) {
+                for (int i = 0; i < beanDefinitionNames.length; i++) {
+                    String beanName = beanDefinitionNames[i];
+                    BeanDefinition beanDefinition = factory.getBeanDefinition(beanName);
+                    ServiceFactory serviceFactory = new SpringServiceFactory((RootBeanDefinition) beanDefinition, objectNameBuilder.getObjectNameMap(), beanFactoryPostProcessors);
+
+                    ObjectName objectName = objectNameBuilder.getObjectName(beanName);
+                    serviceFactories.put(objectName, serviceFactory);
+                }
+            }
+            SpringConfiguration springConfiguration = new SpringConfiguration(kernel, configurationInfo, serviceFactories, classLoader);
+            SimpleServiceFactory springServiceFactory = new SimpleServiceFactory(springConfiguration);
+            ObjectName configurationObjectName = springConfiguration.getObjectName();
+            kernel.loadService(configurationObjectName, springServiceFactory, classLoader);
+            return configurationObjectName;
+        } catch (InvalidConfigurationException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new InvalidConfigurationException("Unable to load configuration: " + location, e);
+        }
+    }
+
+    private String resolveLocation(String location) {
+        location = location.replace('/', '.');
+        return baseDir.toURI().resolve(location).getPath();
+    }
+
+    private ConfigurationInfo loadConfigurationInfo(String location) {
+        ConfigurationInfo configurationInfo = new ConfigurationInfo();
+
+        String gbeanLocation = location + "-gbean.xml";
+
+        File file = new File(gbeanLocation);
+        if (!file.canRead()) {
+            configurationInfo.setConfigurationId(createURI(CONFIG_ID_NAME, location));
+            return configurationInfo;
+        }
+
+        InputStream in = null;
+        try {
+            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+            DocumentBuilder builder = factory.newDocumentBuilder();
+            builder.setErrorHandler(new SimpleSaxErrorHandler(log));
+            in = new FileInputStream(file);
+            Document document = builder.parse(in);
+            loadGBeanConfiguration(configurationInfo, location, document);
+        } catch (ParserConfigurationException e) {
+            throw new InvalidConfigurationException("Parser configuration exception parsing XML from " + gbeanLocation, e);
+        } catch (SAXParseException e) {
+            throw new InvalidConfigurationException("Line " + e.getLineNumber() + " in XML document from " + gbeanLocation + " is invalid", e);
+        } catch (SAXException e) {
+            throw new InvalidConfigurationException("XML document from " + gbeanLocation + " is invalid", e);
+        } catch (IOException ex) {
+            throw new InvalidConfigurationException("IOException parsing XML document from " + gbeanLocation, ex);
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (IOException ex) {
+                    log.warn("Could not close InputStream", ex);
+                }
+            }
+        }
+        return configurationInfo;
+    }
+
+    private void loadGBeanConfiguration(ConfigurationInfo configurationInfo, String location, Document document) {
+        Element root = document.getDocumentElement();
+
+        // get the configuration id... if not specified use the location name
+        String configurationIdString = root.getAttribute(CONFIG_ID_NAME);
+        URI configurationId;
+        if (configurationIdString.length() > 0) {
+            configurationId = createURI(CONFIG_ID_NAME, configurationIdString);
+        } else {
+            configurationId = createURI(CONFIG_ID_NAME, location);
+        }
+        configurationInfo.setConfigurationId(configurationId);
+
+        // set the parent id if specified
+        String parentIdString = root.getAttribute(PARENT_ID_NAME);
+        URI parentId;
+        if (parentIdString.length() > 0) {
+            parentId = createURI(PARENT_ID_NAME, parentIdString);
+            configurationInfo.setParentId(parentId);
+        }
+
+        // set domain name if specifiec
+        String domain = root.getAttribute(DOMAIN_NAME);
+        if (domain.length() > 0) {
+            configurationInfo.setDomain(domain);
+        }
+
+        // set server name if specified
+        String server = root.getAttribute(SERVER_NAME);
+        if (server.length() > 0) {
+            configurationInfo.setServer(server);
+        }
+
+        // add the dependencies
+        // todo add support for more complex maven style dependencies
+        NodeList dependencies = root.getElementsByTagName("dependency");
+        for (int i = 0; i < dependencies.getLength(); i++) {
+            Element dependencyElement = (Element) dependencies.item(i);
+
+            Element uriElement = (Element) dependencyElement.getElementsByTagName("uri").item(0);
+            if (uriElement == null) {
+                throw new RuntimeException("Excpected one uri element in dependency");
+            }
+
+            String uriString = ((Text) uriElement.getFirstChild()).getData().trim();
+            URI dependency = createURI("dependency uri", uriString);
+            configurationInfo.addDependency(dependency);
+        }
+    }
+
+    private static URI createURI(String name, String uri) {
+        try {
+            return new URI(uri);
+        } catch (URISyntaxException e) {
+            throw new InvalidConfigurationException("Unvalid uri specified for " + name, e);
+        }
+    }
+
+    private static class SimpleSaxErrorHandler implements ErrorHandler {
+        private final Log log;
+
+        public SimpleSaxErrorHandler(Log logger) {
+            this.log = logger;
+        }
+
+        public void warning(SAXParseException e) throws SAXException {
+            log.warn("Ignored XML validation warning: " + e.getMessage(), e);
+        }
+
+        public void error(SAXParseException e) throws SAXException {
+            throw e;
+        }
+
+        public void fatalError(SAXParseException e) throws SAXException {
+            throw e;
+        }
+    }
+}
diff --git a/kernel/src/java/org/gbean/spring/SpringServiceFactory.java b/kernel/src/java/org/gbean/spring/SpringServiceFactory.java
new file mode 100644
index 0000000..add3525
--- /dev/null
+++ b/kernel/src/java/org/gbean/spring/SpringServiceFactory.java
@@ -0,0 +1,263 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.spring;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import org.gbean.service.ConfigurableServiceFactory;
+import org.gbean.service.ServiceContext;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.MessageSourceResolvable;
+import org.springframework.context.NoSuchMessageException;
+import org.springframework.context.support.GenericApplicationContext;
+import org.springframework.core.io.Resource;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class SpringServiceFactory implements ConfigurableServiceFactory {
+    private static final String ROOT_BEAN_DEFINITION = "RootBeanDefinition";
+    private final Map dependencies;
+    private final Map objectNameMap;
+    private final List beanFactoryPostProcessors;
+    private RootBeanDefinition beanDefinition;
+    private GenericApplicationContext applicationContext;
+    private boolean enabled = true;
+
+    public SpringServiceFactory(RootBeanDefinition beanDefinition, Map objectNameMap, List beanFactoryPostProcessors) throws Exception {
+        this.beanDefinition = beanDefinition;
+        this.objectNameMap = objectNameMap;
+        this.beanFactoryPostProcessors = beanFactoryPostProcessors;
+        dependencies = SpringUtil.extractDependencies(beanDefinition, objectNameMap);
+    }
+
+    public RootBeanDefinition getBeanDefinition() {
+        return beanDefinition;
+    }
+
+    public void setBeanDefinition(RootBeanDefinition beanDefinition) {
+        this.beanDefinition = beanDefinition;
+    }
+
+    public Map getDependencies() {
+        return dependencies;
+    }
+
+    public void addDependency(String name, Set patterns) {
+        dependencies.put(name, patterns);
+    }
+
+    public Object createService(final ServiceContext serviceContext) throws Exception {
+        Object service = null;
+        try {
+            ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
+            ServiceContext oldServiceContext = ServiceContextThreadLocal.get();
+            try {
+                Thread.currentThread().setContextClassLoader(serviceContext.getClassLoader());
+                ServiceContextThreadLocal.set(serviceContext);
+
+                ApplicationContext parent = new ApplicationContext() {
+                    public ApplicationContext getParent() {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public String getDisplayName() {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public long getStartupDate() {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public void publishEvent(ApplicationEvent event) {
+//                        throw new UnsupportedOperationException();
+                    }
+
+                    public boolean containsBeanDefinition(String beanName) {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public int getBeanDefinitionCount() {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public String[] getBeanDefinitionNames() {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public String[] getBeanDefinitionNames(Class type) {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public String[] getBeanNamesForType(Class type) {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public String[] getBeanNamesForType(Class type, boolean includePrototypes, boolean includeFactoryBeans) {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public Map getBeansOfType(Class type) throws BeansException {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public Map getBeansOfType(Class type, boolean includePrototypes, boolean includeFactoryBeans) throws BeansException {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public Object getBean(String name) throws BeansException {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public Object getBean(String name, Class requiredType) throws BeansException {
+                        ObjectName objectName = null;
+                        try {
+                            objectName = SpringUtil.getObjectName(objectNameMap, name);
+                        } catch (MalformedObjectNameException e) {
+                            throw (NoSuchBeanDefinitionException) new NoSuchBeanDefinitionException(name, "Could not create an objectname for the specified name").initCause(e);
+                        }
+
+                        try {
+                            Object service = serviceContext.getKernel().getService(objectName);
+                            return service;
+                        } catch (Exception e) {
+                            throw (NoSuchBeanDefinitionException) new NoSuchBeanDefinitionException(name, "Kernel threw an exception").initCause(e);
+                        }
+                    }
+
+                    public boolean containsBean(String name) {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public Class getType(String name) throws NoSuchBeanDefinitionException {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public String[] getAliases(String name) throws NoSuchBeanDefinitionException {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public BeanFactory getParentBeanFactory() {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public String getMessage(String code, Object[] args, String defaultMessage, Locale locale) {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public Resource[] getResources(String locationPattern) {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public Resource getResource(String location) {
+                        throw new UnsupportedOperationException();
+                    }
+                };
+
+                // initializae the application context
+                applicationContext = new GenericApplicationContext(parent);
+                for (Iterator iterator = beanFactoryPostProcessors.iterator(); iterator.hasNext();) {
+                    BeanFactoryPostProcessor beanFactoryPostProcessor = (BeanFactoryPostProcessor) iterator.next();
+                    applicationContext.addBeanFactoryPostProcessor(beanFactoryPostProcessor);
+                }
+
+                // copy the bean definition, so we don't modify the original value
+                RootBeanDefinition beanDefinition = new RootBeanDefinition(this.beanDefinition);
+
+                applicationContext.registerBeanDefinition(serviceContext.getObjectName(), beanDefinition);
+                applicationContext.refresh();
+                service = applicationContext.getBean(serviceContext.getObjectName());
+            } finally {
+                Thread.currentThread().setContextClassLoader(oldClassLoader);
+                ServiceContextThreadLocal.set(oldServiceContext);
+            }
+
+        } catch (Throwable t) {
+            applicationContext = null;
+
+            if (t instanceof Exception) {
+                throw (Exception) t;
+            } else if (t instanceof Error) {
+                throw (Error) t;
+            } else {
+                throw new Error(t);
+            }
+        }
+
+        return service;
+    }
+
+    public void destroyService(ServiceContext serviceContext, Object service) {
+        if (applicationContext != null) {
+            applicationContext.close();
+            applicationContext = null;
+        }
+    }
+
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    public Set getPropertyNames() {
+        return Collections.singleton(ROOT_BEAN_DEFINITION);
+    }
+
+    public Object getProperty(String propertyName) {
+        if (ROOT_BEAN_DEFINITION.equals(propertyName)) {
+            return beanDefinition;
+        }
+
+        throw new IllegalArgumentException("Unknown property: " + propertyName);
+    }
+
+    public void setProperty(String propertyName, Object persistentValue) {
+        if (ROOT_BEAN_DEFINITION.equals(propertyName)) {
+            beanDefinition = (RootBeanDefinition) persistentValue;
+        }
+
+        throw new IllegalArgumentException("Unknown property: " + propertyName);
+    }
+}
diff --git a/kernel/src/java/org/gbean/spring/SpringUtil.java b/kernel/src/java/org/gbean/spring/SpringUtil.java
new file mode 100644
index 0000000..a4c5339
--- /dev/null
+++ b/kernel/src/java/org/gbean/spring/SpringUtil.java
@@ -0,0 +1,113 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.spring;
+
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
+import org.springframework.beans.factory.support.BeanDefinitionValidationException;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public final class SpringUtil {
+    private SpringUtil() {
+    }
+
+    public static Map extractDependencies(RootBeanDefinition beanDefinition, final Map objectNameMap) throws Exception {
+        final Map dependencies = new LinkedHashMap();
+        SpringVisitor springVisitor = new AbstractSpringVisitor() {
+            public void visitBeanDefinition(BeanDefinition beanDefinition, Object data) throws BeansException {
+                Map dependencies = (Map) data;
+                super.visitBeanDefinition(beanDefinition, data);
+                if (beanDefinition instanceof RootBeanDefinition) {
+                    RootBeanDefinition rootBeanDefinition = (RootBeanDefinition) beanDefinition;
+                    String[] dependsOn = rootBeanDefinition.getDependsOn();
+                    if (dependsOn != null) {
+                        for (int i = 0; i < dependsOn.length; i++) {
+                            String dependency = dependsOn[i];
+                            try {
+                                dependencies.put(dependency, Collections.singleton(getObjectName(objectNameMap, dependency)));
+                            } catch (MalformedObjectNameException e) {
+                                throw new BeanDefinitionValidationException("Depends on name could not be converted to an objectName: dependency=" + dependency, e);
+                            }
+                        }
+                    }
+
+                    Class beanClass = rootBeanDefinition.getBeanClass();
+                    Method method = null;
+                    try {
+                        method = beanClass.getDeclaredMethod("getDependencies", new Class[]{RootBeanDefinition.class});
+                    } catch (NoSuchMethodException e) {
+                        // ignored mehtod doesn't exist most of the time
+                    }
+                    if (method != null) {
+                        try {
+                            dependencies.putAll((Map) method.invoke(beanClass, new Object[]{rootBeanDefinition}));
+                        } catch (Exception e) {
+                            throw new BeanDefinitionValidationException("Unable to get dependencies from root bean definition: " + rootBeanDefinition, e);
+                        }
+                    }
+                }
+            }
+
+            public void visitRuntimeBeanReference(RuntimeBeanReference beanReference, Object data) throws BeansException {
+                super.visitRuntimeBeanReference(beanReference, data);
+                String dependency = beanReference.getBeanName();
+                try {
+                    Map dependencies = (Map) data;
+                    dependencies.put(dependency, Collections.singleton(getObjectName(objectNameMap, dependency)));
+                } catch (MalformedObjectNameException e) {
+                    throw new BeanDefinitionValidationException("Bean ref name could not be converted to an objectName: refName=" + dependency, e);
+                }
+            }
+
+            public void visitObject(Object value, Object data) throws BeansException {
+                Map dependencies = (Map) data;
+                super.visitObject(value, data);
+                if (value instanceof DependencyProvider) {
+                    DependencyProvider dependencyProvider = (DependencyProvider) value;
+                    dependencies.putAll(dependencyProvider.getDependencies());
+                }
+            }
+        };
+        springVisitor.visitBeanDefinition(beanDefinition, dependencies);
+        return dependencies;
+    }
+
+    public static ObjectName getObjectName(Map objectNameMap, String name) throws MalformedObjectNameException {
+        if (name.indexOf(":") < 0) {
+            ObjectName objectName = (ObjectName) objectNameMap.get(name);
+            if (objectName == null) {
+                throw new NoSuchBeanDefinitionException(name, "No object name definded for service");
+            }
+
+            return objectName;
+        } else {
+            return new ObjectName(name);
+        }
+    }
+}
diff --git a/kernel/src/java/org/gbean/spring/SpringVisitor.java b/kernel/src/java/org/gbean/spring/SpringVisitor.java
new file mode 100644
index 0000000..5791e3f
--- /dev/null
+++ b/kernel/src/java/org/gbean/spring/SpringVisitor.java
@@ -0,0 +1,58 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.spring;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.MutablePropertyValues;
+import org.springframework.beans.PropertyValue;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.config.ConstructorArgumentValues;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public interface SpringVisitor {
+    void visitBeanFactory(ConfigurableListableBeanFactory beanRegistry, Object data) throws BeansException;
+
+    void visitBeanDefinition(String beanName, BeanDefinition beanDefinition, Object data) throws BeansException;
+
+    void visitBeanDefinition(BeanDefinition beanDefinition, Object data) throws BeansException;
+
+    void visitMutablePropertyValues(MutablePropertyValues propertyValues, Object data) throws BeansException;
+
+    void visitConstructorArgumentValues(ConstructorArgumentValues constructorArgumentValues, Object data) throws BeansException;
+
+    void visitConstructorArgumentValue(ConstructorArgumentValues.ValueHolder valueHolder, Object data) throws BeansException;
+
+    void visitPropertyValue(PropertyValue propertyValue, Object data) throws BeansException;
+
+    void visitRuntimeBeanReference(RuntimeBeanReference beanReference, Object data) throws BeansException;
+
+    void visitCollection(Collection collection, Object data)  throws BeansException;
+
+    void visitMap(Map map, Object data)  throws BeansException;
+
+    void visitObject(Object value, Object data) throws BeansException;
+
+    void visitBeanDefinitionHolder(BeanDefinitionHolder beanDefinitionHolder, Object data) throws BeansException;
+}
diff --git a/kernel/src/resources/META-INF/gbean-bootstrap.xml b/kernel/src/resources/META-INF/gbean-bootstrap.xml
new file mode 100644
index 0000000..9894225
--- /dev/null
+++ b/kernel/src/resources/META-INF/gbean-bootstrap.xml
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+* Copyright 2005 the original author or authors.
+*
+*  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.
+-->
+<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
+<beans>
+    <bean id="PropertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
+
+    <bean name="Main" class="org.gbean.kernel.KernelMain" >
+        <property name="kernel" ref="Kernel"/>
+        <property name="services">
+            <map>
+                <entry key=":j2eeType=MetadataManager" value-ref="MetadataManager"/>
+                <entry key=":j2eeType=ServiceInvokerManager,name=default" value-ref="ServiceInvokerManager"/>
+                <entry key=":j2eeType=ProxyManager,name=default" value-ref="ProxyManager"/>
+                <entry key=":j2eeType=Repository,name=FileSystemRepository" value-ref="FileSystemRepository"/>
+                <entry key=":j2eeType=Loader,name=spring" value-ref="SpringLoader"/>
+            </map>
+        </property>
+        <property name="next">
+            <bean class="org.gbean.loader.LoadAllMain">
+                <property name="kernel" ref="Kernel"/>
+            </bean>
+        </property>
+    </bean>
+
+    <bean name="KernelFactory" class="org.gbean.kernel.KernelFactory" factory-method="newInstance"/>
+    <bean name="Kernel" factory-bean="KernelFactory" factory-method="createKernel">
+        <constructor-arg index="0" value="gbean"/>
+    </bean>
+
+    <bean name="FileSystemRepository" class="org.gbean.repository.FileSystemRepository">
+        <property name="root" value="${gbean.base.dir}/repository"/>
+    </bean>
+
+    <bean name="MetadataManager" class="org.gbean.metadata.simple.SimpleMetadataManager">
+        <property name="metadataProviders">
+            <list>
+                <bean class="org.gbean.metadata.simple.PropertiesMetadataProvider"/>
+            </list>
+        </property>        
+    </bean>
+
+    <bean name="ServiceInvokerManager" class="org.gbean.reflect.ServiceInvokerManager" init-method="start" destroy-method="stop">
+        <constructor-arg index="0" ref="Kernel"/>
+    </bean>
+
+    <bean name="ProxyManager" class="org.gbean.proxy.ProxyManager">
+        <constructor-arg index="0" ref="ServiceInvokerManager"/>
+    </bean>
+
+    <bean name="SpringLoader" class="org.gbean.spring.SpringLoader">
+        <property name="kernel" ref="Kernel"/>
+        <property name="repositories"><set><ref bean="FileSystemRepository"/></set></property>
+        <property name="metadataManager" ref="MetadataManager"/>
+        <property name="baseDir" value="${gbean.base.dir}/conf"/>
+        <property name="beanFactoryPostProcessors">
+            <list>
+                <bean class="org.gbean.spring.NamedConstructorArgs">
+                    <property name="metadataManager" ref="MetadataManager"/>
+                    <property name="defaultValues">
+                        <list>
+                            <bean class="org.gbean.spring.DefaultProperty">
+                                <property name="name" value="objectName"/>
+                                <property name="type" value="java.lang.String"/>
+                                <property name="value">
+                                    <bean class="org.gbean.spring.ObjectNameStringReference" factory-method="createBeanDefinition"/>
+                                </property>
+                            </bean>
+                            <bean class="org.gbean.spring.DefaultProperty">
+                                <property name="name" value="objectName"/>
+                                <property name="type" value="javax.management.ObjectName"/>
+                                <property name="value">
+                                    <bean class="org.gbean.spring.ObjectNameReference" factory-method="createBeanDefinition"/>
+                                </property>
+                            </bean>
+                            <bean class="org.gbean.spring.DefaultProperty">
+                                <property name="name" value="classLoader"/>
+                                <property name="type" value="java.lang.ClassLoader"/>
+                                <property name="value">
+                                    <bean class="org.gbean.spring.ClassLoaderReference" factory-method="createBeanDefinition"/>
+                                </property>
+                            </bean>
+                            <bean class="org.gbean.spring.DefaultProperty">
+                                <property name="name" value="kernel"/>
+                                <property name="type" value="org.gbean.kernel.Kernel"/>
+                                <property name="value">
+                                    <bean class="org.gbean.spring.KernelReference" factory-method="createBeanDefinition"/>
+                                </property>
+                            </bean>
+                        </list>
+                    </property>
+                </bean>
+                <bean class="org.gbean.spring.LifecycleDetector">
+                    <property name="lifecycleInterfaces">
+                        <list>
+                            <bean class="org.gbean.spring.LifecycleInfo">
+                                <property name="type" value="org.gbean.kernel.simple.SimpleLifecycle"/>
+                                <property name="initMethodName" value="start"/>
+                                <property name="destroyMethodName" value="stop"/>
+                            </bean>
+                        </list>
+                    </property>
+                </bean>
+            </list>
+        </property>
+    </bean>
+</beans>
diff --git a/kernel/src/resources/META-INF/services/org.apache.geronimo.kernel.KernelFactory b/kernel/src/resources/META-INF/services/org.apache.geronimo.kernel.KernelFactory
new file mode 100644
index 0000000..b713bd2
--- /dev/null
+++ b/kernel/src/resources/META-INF/services/org.apache.geronimo.kernel.KernelFactory
@@ -0,0 +1 @@
+org.gbean.geronimo.KernelBridgeFactory
\ No newline at end of file
diff --git a/kernel/src/test-data/NamedConstructorArgsTest.xml b/kernel/src/test-data/NamedConstructorArgsTest.xml
new file mode 100644
index 0000000..7a2ef2e
--- /dev/null
+++ b/kernel/src/test-data/NamedConstructorArgsTest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+* Copyright 2005 the original author or authors.
+*
+*  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.
+-->
+<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
+<beans>
+    <bean name="hal" class="org.gbean.spring.HelloMessage">
+        <property name="gbean-object-name"><value>foo:name=hello,j2eeType=HelloMessage</value></property>
+        <property name="prefix"><value>I'm sorry </value></property>
+        <property name="suffix"><value>; I'm afraid I can't do that.</value></property>
+    </bean>
+
+    <bean name="foobar" class="org.gbean.spring.HelloMessage" init-method="foo" destroy-method="bar">
+        <property name="prefix"><value>foo </value></property>
+        <property name="suffix"><value> bar</value></property>
+        <property name="properties">
+            <props>
+                <prop key="cheese">yes</prop>
+                <prop key="bacon">yes</prop>
+            </props>
+        </property>
+    </bean>
+</beans>
+
diff --git a/kernel/src/test-data/PostProcessorTest.xml b/kernel/src/test-data/PostProcessorTest.xml
new file mode 100644
index 0000000..7a2ef2e
--- /dev/null
+++ b/kernel/src/test-data/PostProcessorTest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+* Copyright 2005 the original author or authors.
+*
+*  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.
+-->
+<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
+<beans>
+    <bean name="hal" class="org.gbean.spring.HelloMessage">
+        <property name="gbean-object-name"><value>foo:name=hello,j2eeType=HelloMessage</value></property>
+        <property name="prefix"><value>I'm sorry </value></property>
+        <property name="suffix"><value>; I'm afraid I can't do that.</value></property>
+    </bean>
+
+    <bean name="foobar" class="org.gbean.spring.HelloMessage" init-method="foo" destroy-method="bar">
+        <property name="prefix"><value>foo </value></property>
+        <property name="suffix"><value> bar</value></property>
+        <property name="properties">
+            <props>
+                <prop key="cheese">yes</prop>
+                <prop key="bacon">yes</prop>
+            </props>
+        </property>
+    </bean>
+</beans>
+
diff --git a/kernel/src/test-data/server-gbean.xml b/kernel/src/test-data/server-gbean.xml
new file mode 100644
index 0000000..9c8ae1d
--- /dev/null
+++ b/kernel/src/test-data/server-gbean.xml
@@ -0,0 +1,191 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+* Copyright 2005 the original author or authors.
+*
+*  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.
+-->
+<configuration
+    configId="org/apache/geronimo/Server"
+    domain="geronimo.server"
+    server="geronimo"
+    >
+
+    <dependency>
+        <uri>mx4j/jars/mx4j-remote-3.0.1.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>geronimo-spec/jars/geronimo-spec-j2ee-1.4-rc4.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>geronimo-spec/jars/geronimo-spec-j2ee-jacc-1.0-rc4.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>geronimo/jars/geronimo-activation-1.0-SNAPSHOT.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>geronimo/jars/geronimo-j2ee-1.0-SNAPSHOT.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>geronimo/jars/geronimo-core-1.0-SNAPSHOT.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>geronimo/jars/geronimo-connector-1.0-SNAPSHOT.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>concurrent/jars/concurrent-1.3.4.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>geronimo/jars/geronimo-timer-1.0-SNAPSHOT.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>geronimo/jars/geronimo-transaction-1.0-SNAPSHOT.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>geronimo/jars/geronimo-naming-1.0-SNAPSHOT.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>geronimo/jars/geronimo-security-1.0-SNAPSHOT.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>geronimo/jars/geronimo-system-1.0-SNAPSHOT.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>geronimo/jars/geronimo-webservices-1.0-SNAPSHOT.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>tranql/jars/tranql-1.0-SNAPSHOT.jar</uri>
+    </dependency>
+
+    <dependency>
+        <uri>openejb/jars/openejb-core-2.0-SNAPSHOT.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>tmporb/jars/tmporb-orb-1.0-SNAPSHOT.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>tmporb/jars/tmporb-orb-omg-1.0-SNAPSHOT.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>tmporb/jars/tmporb-orb-tools-1.0-SNAPSHOT.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>tmporb/jars/tmporb-tools-1.0-SNAPSHOT.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>tmporb/jars/tmporb-ins-1.0-SNAPSHOT.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>tmporb/jars/tmporb-pss-1.0-SNAPSHOT.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>geronimo-spec/jars/geronimo-spec-corba-2.3-rc4.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>bouncycastle/jars/bcprov-jdk14-124.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>avalon/jars/avalon-framework-4.1.4.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>avalon/jars/avalon-logkit-1.2.2.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>ant/jars/ant-1.5.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>xfire/jars/xfire-20050202.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>xfire/jars/xfire-java-20050202.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>stax/jars/stax-1.1.1-dev.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>stax/jars/stax-api-1.0.jar</uri>
+    </dependency>
+
+
+    <dependency>
+        <uri>geronimo/jars/geronimo-jetty-1.0-SNAPSHOT.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>geronimo/jars/geronimo-jmxremoting-1.0-SNAPSHOT.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>howl/jars/howl-logger-0.1.8.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>antlr/jars/antlr-2.7.2.jar</uri>
+    </dependency>
+
+    <!-- required for rar 1.5 to load realms -->
+    <dependency>
+        <uri>regexp/jars/regexp-1.3.jar</uri>
+    </dependency>
+
+    <!-- for timer serialization implementation -->
+    <dependency>
+        <uri>xstream/jars/xstream-1.0.2.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>xpp3/jars/xpp3-1.1.3.3.jar</uri>
+    </dependency>
+
+    <dependency>
+        <uri>geronimo/jars/geronimo-common-1.0-SNAPSHOT.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>geronimo/jars/geronimo-client-1.0-SNAPSHOT.jar</uri>
+    </dependency>
+
+    <dependency>
+        <uri>asm/jars/asm-1.4.3.jar</uri>
+    </dependency>
+
+    <dependency>
+         <uri>geronimo/jars/geronimo-axis-1.0-SNAPSHOT.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>axis/jars/axis-1.3-SNAPSHOT.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>wsdl4j/jars/wsdl4j-PATCH-1193602.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>commons-discovery/jars/commons-discovery-SNAPSHOT.jar</uri>
+    </dependency>
+
+    <dependency>
+         <uri>geronimo/jars/geronimo-webservices-1.0-SNAPSHOT.jar</uri>
+    </dependency>
+
+    <dependency>
+        <uri>jetty/jars/org.mortbay.jetty-5.1.4rc0.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>tomcat/jars/jasper-compiler-5.5.9.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>tomcat/jars/jasper-compiler-jdt-5.5.9.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>tomcat/jars/jasper-runtime-5.5.9.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>commons-el/jars/commons-el-1.0.jar</uri>
+    </dependency>
+    <dependency>
+        <uri>commons-collections/jars/commons-collections-3.1.jar</uri>
+    </dependency>
+</configuration>
\ No newline at end of file
diff --git a/kernel/src/test-data/server.xml b/kernel/src/test-data/server.xml
new file mode 100644
index 0000000..2be03a8
--- /dev/null
+++ b/kernel/src/test-data/server.xml
@@ -0,0 +1,270 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+* Copyright 2005 the original author or authors.
+*
+*  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.
+-->
+<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
+<beans>
+    <bean name="JMXBridge" class="org.gbean.jmx.JMXBridge">
+        <constructor-arg index="0"><bean class="org.gbean.spring.KernelReference"/></constructor-arg>
+        <constructor-arg index="1"><ref bean=":j2eeType=ServiceInvokerManager,name=default"/></constructor-arg>
+    </bean>
+
+    <bean name="ServerInfo" class="org.apache.geronimo.system.serverinfo.ServerInfo">
+        <property name="gbean-object-name"><value>geronimo.server:J2EEServer=geronimo,J2EEApplication=null,J2EEModule=org/apache/geronimo/System,j2eeType=GBean,name=ServerInfo</value></property>
+    </bean>
+
+    <bean name="URLFactory" class="org.apache.geronimo.system.url.GeronimoURLFactory"/>
+
+    <bean name="LocalConfigurationStore" class="org.apache.geronimo.system.configuration.LocalConfigStore">
+        <property name="gbean-object-name"><value>geronimo.server:J2EEApplication=null,J2EEModule=org/apache/geronimo/System,J2EEServer=geronimo,j2eeType=ConfigurationStore,name=Local</value></property>
+        <property name="root"><value>config-store</value></property>
+        <property name="serverInfo"><ref bean="ServerInfo"/></property>
+    </bean>
+
+    <bean name="GeronimoLoaderBridge" class="org.gbean.geronimo.LoaderBridge">
+        <property name="gbean-object-name"><value>geronimo.server:j2eeType=Loader,name=GeronimoLoaderBridge</value></property>
+        <constructor-arg index="0"><ref bean="LocalConfigurationStore"/></constructor-arg>
+    </bean>
+
+    <bean name="ConfigurationManager" class="org.apache.geronimo.kernel.config.ConfigurationManagerImpl">
+        <property name="stores">
+            <bean class="org.gbean.spring.LiveProxyHashSetReference">
+                <property name="name"><value>Stores</value></property>
+                <property name="pattern"><value>*:j2eeType=ConfigurationStore,*</value></property>
+                <property name="type"><value>org.apache.geronimo.kernel.config.ConfigurationStore</value></property>
+            </bean>
+        </property>
+    </bean>
+
+    <bean name="FileConfigurationList" class="org.apache.geronimo.system.configuration.FileConfigurationList">
+        <property name="serverInfo"><ref bean="ServerInfo"/></property>
+        <property name="configurationManager"><ref bean="ConfigurationManager"/></property>
+        <property name="configFile"><value>var/config/config.list</value></property>
+    </bean>
+
+    <bean name="Repository" class="org.apache.geronimo.system.repository.ReadOnlyRepository">
+        <property name="root"><value>repository/</value></property>
+        <property name="serverInfo"><ref bean="ServerInfo"/></property>
+    </bean>
+
+    <bean name="RMIRegistry" class="org.apache.geronimo.system.rmi.RMIRegistryService">
+        <property name="port"><value>1099</value></property>
+    </bean>
+
+    <bean name="NamingProperties" class="org.apache.geronimo.system.properties.NamingProperties">
+        <property name="namingFactoryInitial"><value>com.sun.jndi.rmi.registry.RegistryContextFactory</value></property>
+        <property name="namingFactoryUrlPkgs"><value>org.apache.geronimo.naming</value></property>
+        <property name="namingProviderUrl"><value>rmi://localhost:1099</value></property>
+    </bean>
+
+    <bean name="properties-login" class="org.apache.geronimo.security.jaas.LoginModuleGBean">
+        <property name="loginModuleClass"><value>org.apache.geronimo.security.realm.providers.PropertiesFileLoginModule</value></property>
+        <property name="serverSide"><value>true</value></property>
+        <property name="options">
+            <props>
+                <prop key="usersURI">var/security/users.properties</prop>
+                <prop key="groupsURI">var/security/groups.properties</prop>
+            </props>
+        </property>
+        <property name="loginDomainName"><value>geronimo-properties-realm</value></property>
+    </bean>
+
+    <bean name="geronimo-properties-realm" class="org.apache.geronimo.security.realm.GenericSecurityRealm">
+        <property name="realmName"><value>geronimo-properties-realm</value></property>
+        <property name="loginModuleConfiguration"><ref bean="properties-login-options"/></property>
+        <property name="serverInfo"><ref bean="ServerInfo"/></property>
+    </bean>
+
+    <bean name="properties-login-options" class="org.apache.geronimo.security.jaas.JaasLoginModuleUse">
+        <property name="loginModule"><ref bean="properties-login"/></property>
+        <property name="controlFlag"><value>REQUIRED</value></property>
+     </bean>
+
+    <bean name="JMX" class="org.apache.geronimo.security.jaas.ServerRealmConfigurationEntry">
+        <property name="applicationConfigName"><value>JMX</value></property>
+        <property name="realmName"><value>geronimo-properties-realm</value></property>
+    </bean>
+
+    <bean name="LoginConfiguration" class="org.apache.geronimo.security.jaas.GeronimoLoginConfiguration">
+        <property name="configurations">
+            <bean class="org.gbean.geronimo.CollectionReference">
+                <property name="name"><value>Configurations</value></property>
+                <property name="patterns">
+                    <set>
+                        <value type="javax.management.ObjectName">*:j2eeType=SecurityRealm,*</value>
+                        <value type="javax.management.ObjectName">*:j2eeType=ConfigurationEntry,*</value>
+                    </set>
+                </property>
+                <property name="type"><value>org.apache.geronimo.security.jaas.ConfigurationEntryFactory</value></property>
+            </bean>
+        </property>
+    </bean>
+
+    <bean name="SecurityService" class="org.apache.geronimo.security.SecurityServiceImpl">
+        <property name="serverInfo"><ref bean="ServerInfo"/></property>
+        <property name="policyConfigurationFactory"><value>org.apache.geronimo.security.jacc.GeronimoPolicyConfigurationFactory</value></property>
+        <property name="policyProvider"><value>org.apache.geronimo.security.jacc.GeronimoPolicy</value></property>
+    </bean>
+
+    <bean name="JaasLoginService" class="org.apache.geronimo.security.jaas.JaasLoginService">
+        <property name="gbean-object-name"><value>geronimo.server:J2EEApplication=null,J2EEModule=org/apache/geronimo/Server,J2EEServer=geronimo,j2eeType=GBean,name=JaasLoginService</value></property>
+        <property name="algorithm"><value>HmacSHA1</value></property>
+        <property name="password"><value>secret</value></property>
+        <property name="realms">
+            <bean class="org.gbean.geronimo.CollectionReference">
+                <property name="name"><value>realms</value></property>
+                <property name="pattern"><value>*:j2eeType=SecurityRealm,*</value></property>
+                <property name="type"><value>org.apache.geronimo.security.realm.SecurityRealm</value></property>
+            </bean>
+        </property>
+    </bean>
+
+    <bean name="DefaultThreadPool" class="org.apache.geronimo.pool.ThreadPool">
+        <property name="poolSize"><value>10</value></property>
+        <property name="poolName"><value>DefaultThreadPool</value></property>
+        <property name="keepAliveTime"><value>5000</value></property>
+    </bean>
+
+    <bean name="ConnectionTracker" class="org.apache.geronimo.connector.outbound.connectiontracking.ConnectionTrackingCoordinator"/>
+
+    <bean name="DefaultWorkManager" class="org.apache.geronimo.connector.work.GeronimoWorkManager">
+        <property name="syncMaximumPoolSize"><value>10</value></property>
+        <property name="startMaximumPoolSize"><value>10</value></property>
+        <property name="scheduledMaximumPoolSize"><value>10</value></property>
+        <property name="transactionContextManager"><ref bean="TransactionContextManager"/></property>
+    </bean>
+
+    <bean name="HOWLTransactionLog" class="org.apache.geronimo.transaction.log.HOWLLog">
+        <property name="bufferClassName"><value>org.objectweb.howl.log.BlockLogBuffer</value></property>
+        <property name="bufferSizeKBytes"><value>32</value></property>
+        <property name="checksumEnabled"><value>true</value></property>
+        <property name="flushSleepTimeMilliseconds"><value>50</value></property>
+        <property name="logFileDir"><value>var/txlog</value></property>
+        <property name="logFileExt"><value>log</value></property>
+        <property name="logFileName"><value>howl</value></property>
+        <property name="maxBlocksPerFile"><value>-1</value></property>
+        <property name="maxBuffers"><value>0</value></property>
+        <property name="maxLogFiles"><value>2</value></property>
+        <property name="minBuffers"><value>4</value></property>
+        <property name="threadsWaitingForceThreshold"><value>-1</value></property>
+        <property name="serverInfo"><ref bean="ServerInfo"/></property>
+    </bean>
+
+    <bean name="TransactionManager" class="org.apache.geronimo.transaction.manager.TransactionManagerImpl">
+        <property name="defaultTransactionTimeoutSeconds"><value>600</value></property>
+        <property name="transactionLog"><ref bean="HOWLTransactionLog"/></property>
+        <property name="resourceManagers">
+            <bean class="org.gbean.geronimo.CollectionReference">
+                <property name="name"><value>ResourceManagers</value></property>
+                <property name="patterns">
+                    <set>
+                        <value type="javax.management.ObjectName">*:j2eeType=JCAManagedConnectionFactory,*</value>
+                        <value type="javax.management.ObjectName">*:j2eeType=ActivationSpec,*</value>
+                    </set>
+                </property>
+                <property name="type"><value>org.apache.geronimo.transaction.manager.ResourceManager</value></property>
+            </bean>
+        </property>
+    </bean>
+
+    <bean name="TransactionContextManager" class="org.apache.geronimo.transaction.context.TransactionContextManager">
+        <property name="transactionManager"><ref bean="TransactionManager"/></property>
+        <property name="xidImporter"><ref bean="TransactionManager"/></property>
+    </bean>
+
+    <bean name="JettyWebContainer" class="org.apache.geronimo.jetty.JettyContainerImpl"/>
+
+    <bean name="JettyRequestLog" class="org.apache.geronimo.jetty.requestlog.NCSARequestLog">
+        <property name="jettyContainer"><ref bean="JettyWebContainer"/></property>
+        <property name="serverInfo"><ref bean="ServerInfo"/></property>
+        <property name="filename"><value>var/log/jetty_yyyy_mm_dd.log</value></property>
+        <property name="logDateFormat"><value>dd/MMM/yyyy:HH:mm:ss ZZZ</value></property>
+        <property name="logTimeZone"><value>GMT</value></property>
+    </bean>
+
+    <bean name="JettyWebConnector" class="org.apache.geronimo.jetty.connector.HTTPConnector">
+        <property name="jettyContainer"><ref bean="JettyWebContainer"/></property>
+        <property name="port"><value>8080</value></property>
+    </bean>
+
+    <bean name="JettySSLConnector" class="org.apache.geronimo.jetty.connector.HTTPSConnector">
+        <property name="jettyContainer"><ref bean="JettyWebContainer"/></property>
+        <property name="serverInfo"><ref bean="ServerInfo"/></property>
+        <property name="port"><value>8443</value></property>
+        <property name="keystore"><value>var/security/keystore</value></property>
+        <property name="keystoreType"><value>JKS</value></property>
+        <property name="password"><value>secret</value></property>
+        <property name="keyPassword"><value>secret</value></property>
+        <property name="needClientAuth"><value>false</value></property>
+        <property name="protocol"><value>TLS</value></property>
+    </bean>
+
+    <bean name="EjbNetworkService" class="org.openejb.server.StandardServiceStack">
+        <property name="name"><value>EJB</value></property>
+        <property name="port"><value>4201</value></property>
+        <property name="address"><value>127.0.0.1</value></property>
+        <property name="allowHosts"><value>127.0.0.1</value></property>
+        <property name="logOnSuccess"><value>HOST,NAME,THREADID,USERID</value></property>
+        <property name="logOnFailure"><value>HOST,NAME</value></property>
+        <property name="executor"><ref bean="DefaultThreadPool"/></property>
+        <property name="server"><ref bean="EjbServer"/></property>
+    </bean>
+    
+    <bean name="EjbServer" class="org.openejb.server.ejbd.EjbServer">
+        <property name="containerIndex"><ref bean="ContainerIndex"/></property>
+    </bean>
+    
+    <bean name="ContainerIndex" class="org.openejb.ContainerIndex">
+        <property name="eJBContainers">
+            <bean class="org.gbean.geronimo.CollectionReference">
+                <property name="name"><value>ResourceManagers</value></property>
+                <property name="patterns">
+                    <set>
+                        <value type="javax.management.ObjectName">*:j2eeType=StatelessSessionBean,*</value>
+                        <value type="javax.management.ObjectName">*:j2eeType=StatefulSessionBean,*</value>
+                        <value type="javax.management.ObjectName">*:j2eeType=EntityBean,*</value>
+                    </set>
+                </property>
+                <property name="type"><value>org.openejb.EJBContainer</value></property>
+            </bean>
+        </property>
+    </bean>
+
+    <bean name="JMXService" class="org.apache.geronimo.jmxremoting.JMXConnector" depends-on="NamingProperties, RMIRegistry">
+        <property name="url"><value>service:jmx:rmi://localhost/jndi/rmi:/JMXConnector</value></property>
+        <property name="applicationConfigName"><value>JMX</value></property>
+    </bean>
+
+    <bean name="eis/JAXR" class="org.apache.geronimo.webservices.jaxr.JAXRGBean"/>
+
+    <bean name="J2EEDomain" class="org.apache.geronimo.j2ee.management.impl.J2EEDomainImpl">
+        <property name="gbean-object-name"><value>geronimo.server:j2eeType=J2EEDomain,name=geronimo.server</value></property>
+    </bean>
+
+    <bean name="J2EEServer" class="org.apache.geronimo.j2ee.management.impl.J2EEServerImpl">
+        <property name="gbean-object-name"><value>geronimo.server:j2eeType=J2EEServer,name=geronimo</value></property>
+        <property name="serverInfo"><ref bean="ServerInfo"/></property>
+    </bean>
+
+    <bean name="JVM" class="org.apache.geronimo.j2ee.management.impl.JVMImpl">
+        <property name="gbean-object-name"><value>geronimo.server:j2eeType=JVM,J2EEServer=geronimo,name=JVM</value></property>
+    </bean>
+
+    <bean name="MEJB" class="org.openejb.mejb.MEJB">
+        <property name="gbean-object-name"><value>geronimo.server:J2EEApplication=null,EJBModule=org/apache/geronimo/Server,J2EEServer=geronimo,j2eeType=StatelessSessionBean,name=ejb/mgmt/MEJB</value></property>
+    </bean>
+
+</beans>
+
diff --git a/kernel/src/test/org/gbean/classloader/JarFileClassLoaderTest.java b/kernel/src/test/org/gbean/classloader/JarFileClassLoaderTest.java
new file mode 100644
index 0000000..fc127a0
--- /dev/null
+++ b/kernel/src/test/org/gbean/classloader/JarFileClassLoaderTest.java
@@ -0,0 +1,156 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.classloader;
+
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.net.URL;
+
+import junit.framework.TestCase;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class JarFileClassLoaderTest extends TestCase {
+    private static final String ENTRY_NAME = "foo";
+    private static final String ENTRY_VALUE = "bar";
+    private File file;
+    private static final String NON_EXISTANT_RESOURCE = "non-existant-resource";
+
+    // this stuff doesn't work on windows
+    public void testNothing() {
+    }
+
+    public void XtestReadEntry() throws Exception {
+        JarFile jarFile = new JarFile(file);
+        JarEntry jarEntry = jarFile.getJarEntry(ENTRY_NAME);
+        String urlString = "jar:" + file.toURL() + "!/" + ENTRY_NAME;
+        URL url = new URL(file.toURL(), urlString);
+        assertStreamContains(ENTRY_VALUE, url.openStream());
+        jarFile.close();
+    }
+
+    public void XtestGetResourceAsStream() throws Exception {
+        JarFileClassLoader classLoader = new JarFileClassLoader("test", Collections.singletonList(file.toURL()));
+        InputStream in = classLoader.getResourceAsStream(ENTRY_NAME);
+        assertStreamContains(ENTRY_VALUE, in);
+
+        classLoader.destroy();
+    }
+
+    public void XtestGetNonExistantResourceAsStream() throws Exception {
+        JarFileClassLoader classLoader = new JarFileClassLoader("test", Collections.singletonList(file.toURL()));
+        InputStream in = classLoader.getResourceAsStream(NON_EXISTANT_RESOURCE);
+        assertNull(in);
+
+        classLoader.destroy();
+    }
+
+    public void XtestGetResource() throws Exception {
+        JarFileClassLoader classLoader = new JarFileClassLoader("test", Collections.singletonList(file.toURL()));
+        URL resource = classLoader.getResource(ENTRY_NAME);
+        assertNotNull(resource);
+
+        InputStream in = resource.openStream();
+        assertStreamContains(ENTRY_VALUE, in);
+
+        classLoader.destroy();
+    }
+
+    public void XtestGetNonExistantResource() throws Exception {
+        JarFileClassLoader classLoader = new JarFileClassLoader("test", Collections.singletonList(file.toURL()));
+        URL resource = classLoader.getResource(NON_EXISTANT_RESOURCE);
+        assertNull(resource);
+
+        classLoader.destroy();
+    }
+
+    public void XtestGetResources() throws Exception {
+        JarFileClassLoader classLoader = new JarFileClassLoader("test", Collections.singletonList(file.toURL()));
+        Enumeration resources = classLoader.getResources(ENTRY_NAME);
+        assertNotNull(resources);
+        assertTrue(resources.hasMoreElements());
+
+        URL resource = (URL) resources.nextElement();
+        assertNotNull(resource);
+
+        InputStream in = resource.openStream();
+        assertStreamContains(ENTRY_VALUE, in);
+
+        classLoader.destroy();
+    }
+
+    public void XtestGetNonExistantResources() throws Exception {
+        JarFileClassLoader classLoader = new JarFileClassLoader("test", Collections.singletonList(file.toURL()));
+        Enumeration resources = classLoader.getResources(NON_EXISTANT_RESOURCE);
+        assertNotNull(resources);
+        assertFalse(resources.hasMoreElements());
+
+        classLoader.destroy();
+    }
+
+    private void assertStreamContains(String expectedValue, InputStream in) throws IOException {
+        String entryValue;
+        try {
+            StringBuffer stringBuffer = new StringBuffer();
+            byte[] bytes = new byte[4000];
+            for (int count = in.read(bytes); count != -1; count = in.read(bytes)) {
+                stringBuffer.append(new String(bytes, 0, count));
+            }
+            entryValue = stringBuffer.toString();
+        } finally {
+            in.close();
+        }
+        assertEquals(expectedValue, entryValue);
+    }
+
+    private static void assertFileExists(File file) {
+        assertTrue("File should exist: " + file, file.canRead());
+    }
+
+    private static void assertFileNotExists(File file) {
+        assertTrue("File should not exist: " + file, !file.canRead());
+    }
+
+    protected void setUp() throws Exception {
+        file = new File("test.jar");
+        file.delete();
+
+        assertFileNotExists(file);
+
+        FileOutputStream out = new FileOutputStream(file);
+        JarOutputStream jarOut = new JarOutputStream(out);
+        jarOut.putNextEntry(new JarEntry(ENTRY_NAME));
+        jarOut.write(ENTRY_VALUE.getBytes());
+        jarOut.close();
+        out.close();
+
+        assertFileExists(file);
+    }
+
+    protected void tearDown() throws Exception {
+        file.delete();
+        assertFileNotExists(file);
+    }
+}
diff --git a/kernel/src/test/org/gbean/metadata/simple/LotsOfTypes.java b/kernel/src/test/org/gbean/metadata/simple/LotsOfTypes.java
new file mode 100644
index 0000000..89d06db
--- /dev/null
+++ b/kernel/src/test/org/gbean/metadata/simple/LotsOfTypes.java
@@ -0,0 +1,244 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.metadata.simple;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class LotsOfTypes {
+    private byte b;
+    private short s;
+    private int i;
+    private long l;
+    private float f;
+    private double d;
+    private char c;
+    private boolean bool;
+    private String string;
+
+    private byte[] bArray;
+    private short[] sArray;
+    private int[] iArray;
+    private long[] lArray;
+    private float[] fArray;
+    private double[] dArray;
+    private char[] cArray;
+    private boolean[] boolArray;
+    private String[] stringArray;
+
+    public LotsOfTypes() {
+    }
+
+    public LotsOfTypes(byte b, short s, int i, long l, float f, double d, char c, boolean bool, String string) {
+        this.b = b;
+        this.s = s;
+        this.i = i;
+        this.l = l;
+        this.f = f;
+        this.d = d;
+        this.c = c;
+        this.bool = bool;
+        this.string = string;
+    }
+
+    public LotsOfTypes(byte[] bArray, short[] sArray, int[] iArray, long[] lArray, float[] fArray, double[] dArray, char[] cArray, boolean[] boolArray, String[] stringArray) {
+        this.bArray = bArray;
+        this.sArray = sArray;
+        this.iArray = iArray;
+        this.lArray = lArray;
+        this.fArray = fArray;
+        this.dArray = dArray;
+        this.cArray = cArray;
+        this.boolArray = boolArray;
+        this.stringArray = stringArray;
+    }
+
+    public LotsOfTypes(byte b, short s, int i, long l, float f, double d, char c, boolean bool, String string, byte[] bArray, short[] sArray, int[] iArray, long[] lArray, float[] fArray, double[] dArray, char[] cArray, boolean[] boolArray, String[] stringArray) {
+        this.b = b;
+        this.s = s;
+        this.i = i;
+        this.l = l;
+        this.f = f;
+        this.d = d;
+        this.c = c;
+        this.bool = bool;
+        this.string = string;
+        this.bArray = bArray;
+        this.sArray = sArray;
+        this.iArray = iArray;
+        this.lArray = lArray;
+        this.fArray = fArray;
+        this.dArray = dArray;
+        this.cArray = cArray;
+        this.boolArray = boolArray;
+        this.stringArray = stringArray;
+    }
+
+    public void allBasic(byte b, short s, int i, long l, float f, double d, char c, boolean bool, String string) {
+    }
+
+    public void allArray(byte[] bArray, short[] sArray, int[] iArray, long[] lArray, float[] fArray, double[] dArray, char[] cArray, boolean[] boolArray, String[] stringArray) {
+    }
+
+    public void all(byte b, short s, int i, long l, float f, double d, char c, boolean bool, String string, byte[] bArray, short[] sArray, int[] iArray, long[] lArray, float[] fArray, double[] dArray, char[] cArray, boolean[] boolArray, String[] stringArray) {
+    }
+
+
+    public byte getB() {
+        return b;
+    }
+
+    public void setB(byte b) {
+        this.b = b;
+    }
+
+    public short getS() {
+        return s;
+    }
+
+    public void setS(short s) {
+        this.s = s;
+    }
+
+    public int getI() {
+        return i;
+    }
+
+    public void setI(int i) {
+        this.i = i;
+    }
+
+    public long getL() {
+        return l;
+    }
+
+    public void setL(long l) {
+        this.l = l;
+    }
+
+    public float getF() {
+        return f;
+    }
+
+    public void setF(float f) {
+        this.f = f;
+    }
+
+    public double getD() {
+        return d;
+    }
+
+    public void setD(double d) {
+        this.d = d;
+    }
+
+    public char getC() {
+        return c;
+    }
+
+    public void setC(char c) {
+        this.c = c;
+    }
+
+    public boolean isBool() {
+        return bool;
+    }
+
+    public void setBool(boolean bool) {
+        this.bool = bool;
+    }
+
+    public String getString() {
+        return string;
+    }
+
+    public void setString(String string) {
+        this.string = string;
+    }
+
+    public byte[] getbArray() {
+        return bArray;
+    }
+
+    public void setbArray(byte[] bArray) {
+        this.bArray = bArray;
+    }
+
+    public short[] getsArray() {
+        return sArray;
+    }
+
+    public void setsArray(short[] sArray) {
+        this.sArray = sArray;
+    }
+
+    public int[] getiArray() {
+        return iArray;
+    }
+
+    public void setiArray(int[] iArray) {
+        this.iArray = iArray;
+    }
+
+    public long[] getlArray() {
+        return lArray;
+    }
+
+    public void setlArray(long[] lArray) {
+        this.lArray = lArray;
+    }
+
+    public float[] getfArray() {
+        return fArray;
+    }
+
+    public void setfArray(float[] fArray) {
+        this.fArray = fArray;
+    }
+
+    public double[] getdArray() {
+        return dArray;
+    }
+
+    public void setdArray(double[] dArray) {
+        this.dArray = dArray;
+    }
+
+    public char[] getcArray() {
+        return cArray;
+    }
+
+    public void setcArray(char[] cArray) {
+        this.cArray = cArray;
+    }
+
+    public boolean[] getBoolArray() {
+        return boolArray;
+    }
+
+    public void setBoolArray(boolean[] boolArray) {
+        this.boolArray = boolArray;
+    }
+
+    public String[] getStringArray() {
+        return stringArray;
+    }
+
+    public void setStringArray(String[] stringArray) {
+        this.stringArray = stringArray;
+    }
+}
diff --git a/kernel/src/test/org/gbean/metadata/simple/LotsOfTypes.properties b/kernel/src/test/org/gbean/metadata/simple/LotsOfTypes.properties
new file mode 100644
index 0000000..2e9e2da
--- /dev/null
+++ b/kernel/src/test/org/gbean/metadata/simple/LotsOfTypes.properties
@@ -0,0 +1,155 @@
+# LotsOfTypes()
+
+description=class description
+LotsOfTypes().description = constructor description
+getB().description = method description
+
+LotsOfTypes(byte, short, int, long, float, double, char, boolean, java.lang.String).0.name = b
+LotsOfTypes(byte, short, int, long, float, double, char, boolean, java.lang.String).1.name = s
+LotsOfTypes(byte, short, int, long, float, double, char, boolean, java.lang.String).2.name = i
+LotsOfTypes(byte, short, int, long, float, double, char, boolean, java.lang.String).3.name = l
+LotsOfTypes(byte, short, int, long, float, double, char, boolean, java.lang.String).4.name = f
+LotsOfTypes(byte, short, int, long, float, double, char, boolean, java.lang.String).5.name = d
+LotsOfTypes(byte, short, int, long, float, double, char, boolean, java.lang.String).6.name = c
+LotsOfTypes(byte, short, int, long, float, double, char, boolean, java.lang.String).7.name = bool
+LotsOfTypes(byte, short, int, long, float, double, char, boolean, java.lang.String).8.name = string
+
+LotsOfTypes(byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).0.name = bArray
+LotsOfTypes(byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).1.name = sArray
+LotsOfTypes(byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).2.name = iArray
+LotsOfTypes(byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).3.name = lArray
+LotsOfTypes(byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).4.name = fArray
+LotsOfTypes(byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).5.name = dArray
+LotsOfTypes(byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).6.name = cArray
+LotsOfTypes(byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).7.name = boolArray
+LotsOfTypes(byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).8.name = stringArray
+
+LotsOfTypes(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).0.name = b
+LotsOfTypes(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).1.name = s
+LotsOfTypes(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).2.name = i
+LotsOfTypes(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).3.name = l
+LotsOfTypes(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).4.name = f
+LotsOfTypes(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).5.name = d
+LotsOfTypes(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).6.name = c
+LotsOfTypes(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).7.name = bool
+LotsOfTypes(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).8.name = string
+LotsOfTypes(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).9.name = bArray
+LotsOfTypes(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).10.name = sArray
+LotsOfTypes(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).11.name = iArray
+LotsOfTypes(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).12.name = lArray
+LotsOfTypes(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).13.name = fArray
+LotsOfTypes(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).14.name = dArray
+LotsOfTypes(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).15.name = cArray
+LotsOfTypes(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).16.name = boolArray
+LotsOfTypes(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).17.name = stringArray
+
+allBasic(byte, short, int, long, float, double, char, boolean, java.lang.String).0.name = b
+allBasic(byte, short, int, long, float, double, char, boolean, java.lang.String).1.name = s
+allBasic(byte, short, int, long, float, double, char, boolean, java.lang.String).2.name = i
+allBasic(byte, short, int, long, float, double, char, boolean, java.lang.String).3.name = l
+allBasic(byte, short, int, long, float, double, char, boolean, java.lang.String).4.name = f
+allBasic(byte, short, int, long, float, double, char, boolean, java.lang.String).5.name = d
+allBasic(byte, short, int, long, float, double, char, boolean, java.lang.String).6.name = c
+allBasic(byte, short, int, long, float, double, char, boolean, java.lang.String).7.name = bool
+allBasic(byte, short, int, long, float, double, char, boolean, java.lang.String).8.name = string
+
+allArray(byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).0.name = bArray
+allArray(byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).1.name = sArray
+allArray(byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).2.name = iArray
+allArray(byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).3.name = lArray
+allArray(byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).4.name = fArray
+allArray(byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).5.name = dArray
+allArray(byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).6.name = cArray
+allArray(byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).7.name = boolArray
+allArray(byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).8.name = stringArray
+
+all(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).0.name = b
+all(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).1.name = s
+all(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).2.name = i
+all(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).3.name = l
+all(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).4.name = f
+all(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).5.name = d
+all(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).6.name = c
+all(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).7.name = bool
+all(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).8.name = string
+all(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).9.name = bArray
+all(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).10.name = sArray
+all(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).11.name = iArray
+all(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).12.name = lArray
+all(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).13.name = fArray
+all(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).14.name = dArray
+all(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).15.name = cArray
+all(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).16.name = boolArray
+all(byte, short, int, long, float, double, char, boolean, java.lang.String, byte[], short[], int[], long[], float[], double[], char[], boolean[], java.lang.String[]).17.name = stringArray
+
+# getB()
+
+setB(byte).0.name = b
+
+# getS()
+
+setS(short).0.name = s
+
+# getI()
+
+setI(int).0.name = i
+
+# getL()
+
+setL(long).0.name = l
+
+# getF()
+
+setF(float).0.name = f
+
+# getD()
+
+setD(double).0.name = d
+
+# getC()
+
+setC(char).0.name = c
+
+# boolean isBool()
+
+setBool(boolean).0.name = bool
+
+# getString()
+
+setString(java.lang.String).0.name = string
+
+# getbArray()
+
+setbArray(byte[]).0.name = bArray
+
+# getsArray()
+
+setsArray(short[]).0.name = sArray
+
+# getiArray()
+
+setiArray(int[]).0.name = iArray
+
+# getlArray()
+
+setlArray(long[]).0.name = lArray
+
+# getfArray()
+
+setfArray(float[]).0.name = fArray
+
+# getdArray()
+
+setdArray(double[]).0.name = dArray
+
+# getcArray()
+
+setcArray(char[]).0.name = cArray
+
+# getBoolArray()
+
+setBoolArray(boolean[]).0.name = boolArray
+
+# getStringArray()
+
+setStringArray(java.lang.String[]).0.name = stringArray
diff --git a/kernel/src/test/org/gbean/metadata/simple/PropertiesMetadataProviderTest.java b/kernel/src/test/org/gbean/metadata/simple/PropertiesMetadataProviderTest.java
new file mode 100644
index 0000000..1e0176f
--- /dev/null
+++ b/kernel/src/test/org/gbean/metadata/simple/PropertiesMetadataProviderTest.java
@@ -0,0 +1,105 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.metadata.simple;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+import org.gbean.kernel.ConstructorSignature;
+import org.gbean.kernel.OperationSignature;
+import org.gbean.metadata.ConstructorMetadata;
+import org.gbean.metadata.MethodMetadata;
+import org.gbean.metadata.ParameterMetadata;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class PropertiesMetadataProviderTest extends TestCase {
+    private static final Map NAMES = new HashMap();
+    static {
+        NAMES.put(int.class, "i");
+        NAMES.put(byte.class, "b");
+        NAMES.put(short.class, "s");
+        NAMES.put(int.class, "i");
+        NAMES.put(long.class, "l");
+        NAMES.put(float.class, "f");
+        NAMES.put(double.class, "d");
+        NAMES.put(char.class, "c");
+        NAMES.put(boolean.class, "bool");
+        NAMES.put(String.class, "string");
+        NAMES.put(byte[].class, "bArray");
+        NAMES.put(short[].class, "sArray");
+        NAMES.put(int[].class, "iArray");
+        NAMES.put(long[].class, "lArray");
+        NAMES.put(float[].class, "fArray");
+        NAMES.put(double[].class, "dArray");
+        NAMES.put(char[].class, "cArray");
+        NAMES.put(boolean[].class, "boolArray");
+        NAMES.put(String[].class, "stringArray");
+    }
+
+    public void testLoad() {
+        PropertiesMetadataProvider propertiesMetadataProvider = new PropertiesMetadataProvider();
+        Class type = LotsOfTypes.class;
+        SimpleClassMetadata classMetadata = new SimpleClassMetadata(type);
+        propertiesMetadataProvider.addClassMetadata(classMetadata);
+        assertNotNull(classMetadata);
+
+        Constructor[] constructors = type.getDeclaredConstructors();
+        for (int i = 0; i < constructors.length; i++) {
+            Constructor constructor = constructors[i];
+            ConstructorMetadata constructorMetadata = classMetadata.getConstructor(constructor);
+            assertNotNull(constructorMetadata);
+            assertEquals(new ConstructorSignature(constructor), constructorMetadata.getSignature());
+            assertEquals(constructor.getParameterTypes().length, constructorMetadata.getParameters().size());
+            verifyParameterNames(constructor.getParameterTypes(), constructorMetadata.getParameters());
+        }
+
+        Method[] methods = type.getDeclaredMethods();
+        for (int i = 0; i < methods.length; i++) {
+            Method method = methods[i];
+            MethodMetadata methodMetadata = classMetadata.getMethod(method);
+            assertNotNull(methodMetadata);
+            assertEquals(new OperationSignature(method), methodMetadata.getSignature());
+            assertEquals(method.getParameterTypes().length, methodMetadata.getParameters().size());
+            verifyParameterNames(method.getParameterTypes(), methodMetadata.getParameters());
+        }
+
+        assertEquals(classMetadata.get("description"), "class description");
+        assertEquals(classMetadata.getConstructor(new ConstructorSignature(new String[]{})).get("description"), "constructor description");
+        assertEquals(classMetadata.getMethod(new OperationSignature("getB", new String[]{})).get("description"), "method description");
+
+    }
+
+    private void verifyParameterNames(Class[] parameterTypes, List parameterMetadata) {
+        for (int i = 0; i < parameterTypes.length; i++) {
+            Class parameterType = parameterTypes[i];
+            ParameterMetadata metadata = (ParameterMetadata) parameterMetadata.get(i);
+            assertEquals(i, metadata.getIndex());
+            assertEquals(parameterType, metadata.getType());
+            String name = (String) metadata.get("name");
+            assertNotNull(name);
+            String typeName = (String) NAMES.get(parameterType);
+            assertNotNull(typeName);
+            assertEquals(typeName, name);
+        }
+    }
+}
diff --git a/kernel/src/test/org/gbean/spring/HelloMessage.java b/kernel/src/test/org/gbean/spring/HelloMessage.java
new file mode 100644
index 0000000..77a854d
--- /dev/null
+++ b/kernel/src/test/org/gbean/spring/HelloMessage.java
@@ -0,0 +1,76 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.spring;
+
+import java.util.Properties;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class HelloMessage implements MyLifecycle {
+    private String prefix;
+    private String suffix;
+    private Properties properties;
+
+    public HelloMessage() {
+    }
+
+    public HelloMessage(String prefix, String suffix) {
+        this.prefix = prefix;
+        this.suffix = suffix;
+    }
+
+    public HelloMessage(String prefix, String suffix, Properties properties) {
+        this.prefix = prefix;
+        this.suffix = suffix;
+        this.properties = properties;
+    }
+
+    public String getPrefix() {
+        return prefix;
+    }
+
+    public void setPrefix(String prefix) {
+        this.prefix = prefix;
+    }
+
+    public String getSuffix() {
+        return suffix;
+    }
+
+    public void setSuffix(String suffix) {
+        this.suffix = suffix;
+    }
+
+    public String message(String message) {
+        return prefix + message + suffix;
+    }
+
+    public Properties getProperties() {
+        return properties;
+    }
+
+    public void setProperties(Properties properties) {
+        this.properties = properties;
+    }
+
+    public void begin() {
+    }
+
+    public void end() {
+    }
+}
diff --git a/kernel/src/test/org/gbean/spring/HelloMessage.properties b/kernel/src/test/org/gbean/spring/HelloMessage.properties
new file mode 100644
index 0000000..15e088d
--- /dev/null
+++ b/kernel/src/test/org/gbean/spring/HelloMessage.properties
@@ -0,0 +1,9 @@
+HelloMessage(java.lang.String, java.lang.String).0.name = prefix
+HelloMessage(java.lang.String, java.lang.String).1.name = suffix
+HelloMessage(java.lang.String, java.lang.String, java.util.Properties).0.name = prefix
+HelloMessage(java.lang.String, java.lang.String, java.util.Properties).1.name = suffix
+HelloMessage(java.lang.String, java.lang.String, java.util.Properties).2.name = properties
+setPrefix(java.lang.String).0.name = prefix
+setSuffix(java.lang.String).0.name = suffix
+message(java.lang.String).0.name = message
+setProperties(java.util.Properties).0.name = properties
diff --git a/kernel/src/test/org/gbean/spring/LifecycleDetectorTest.java b/kernel/src/test/org/gbean/spring/LifecycleDetectorTest.java
new file mode 100644
index 0000000..d3c85ff
--- /dev/null
+++ b/kernel/src/test/org/gbean/spring/LifecycleDetectorTest.java
@@ -0,0 +1,68 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.spring;
+
+import java.io.File;
+
+import junit.framework.TestCase;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.Resource;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class LifecycleDetectorTest extends TestCase {
+    private DefaultListableBeanFactory factory;
+
+    public void testLifecycleDetection() {
+        String beanName = "hal";
+        assertTrue(factory.containsBeanDefinition(beanName));
+        RootBeanDefinition beanDefinition = (RootBeanDefinition) factory.getBeanDefinition(beanName);
+        assertNotNull(beanDefinition);
+
+        LifecycleDetector lifecycleDetector = new LifecycleDetector();
+        lifecycleDetector.addLifecycleInterface(MyLifecycle.class, "begin", "end");
+        lifecycleDetector.postProcessBeanFactory(factory);
+
+        assertEquals("begin", beanDefinition.getInitMethodName());
+        assertEquals("end", beanDefinition.getDestroyMethodName());
+    }
+
+    public void testObjectNameCreation() {
+        String beanName = "foobar";
+        assertTrue(factory.containsBeanDefinition(beanName));
+        RootBeanDefinition beanDefinition = (RootBeanDefinition) factory.getBeanDefinition(beanName);
+        assertNotNull(beanDefinition);
+
+        LifecycleDetector lifecycleDetector = new LifecycleDetector();
+        lifecycleDetector.addLifecycleInterface(MyLifecycle.class, "begin", "end");
+        lifecycleDetector.postProcessBeanFactory(factory);
+
+        assertEquals("foo", beanDefinition.getInitMethodName());
+        assertEquals("bar", beanDefinition.getDestroyMethodName());
+    }
+
+    protected void setUp() throws Exception {
+        factory = new DefaultListableBeanFactory();
+        Resource resource = new FileSystemResource(new File("src/test-data/PostProcessorTest.xml"));
+        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
+        reader.loadBeanDefinitions(resource);
+    }
+}
diff --git a/kernel/src/test/org/gbean/spring/MyLifecycle.java b/kernel/src/test/org/gbean/spring/MyLifecycle.java
new file mode 100644
index 0000000..395b1a7
--- /dev/null
+++ b/kernel/src/test/org/gbean/spring/MyLifecycle.java
@@ -0,0 +1,25 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.spring;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public interface MyLifecycle {
+    void begin();
+    void end();
+}
diff --git a/kernel/src/test/org/gbean/spring/NamedConstructorArgsTest.java b/kernel/src/test/org/gbean/spring/NamedConstructorArgsTest.java
new file mode 100644
index 0000000..0e42756
--- /dev/null
+++ b/kernel/src/test/org/gbean/spring/NamedConstructorArgsTest.java
@@ -0,0 +1,141 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.spring;
+
+import java.io.File;
+import java.util.Properties;
+import java.util.Collections;
+
+import junit.framework.TestCase;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
+import org.springframework.beans.factory.config.ConstructorArgumentValues;
+import org.springframework.beans.MutablePropertyValues;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.Resource;
+import org.gbean.metadata.simple.PropertiesMetadataProvider;
+import org.gbean.metadata.simple.SimpleMetadataManager;
+import org.gbean.metadata.ClassMetadata;
+import org.gbean.metadata.ConstructorMetadata;
+import org.gbean.metadata.MetadataManager;
+import org.gbean.kernel.ConstructorSignature;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class NamedConstructorArgsTest extends TestCase {
+    private DefaultListableBeanFactory factory;
+
+    public void testSimplePropertiesConversion() {
+        // pre conditions
+        String beanName = "hal";
+        assertTrue(factory.containsBeanDefinition(beanName));
+        RootBeanDefinition beanDefinition = (RootBeanDefinition) factory.getBeanDefinition(beanName);
+        assertNotNull(beanDefinition);
+
+        ConstructorArgumentValues constructorArgumentValues = beanDefinition.getConstructorArgumentValues();
+        assertEquals(0, constructorArgumentValues.getArgumentCount());
+
+        MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
+        assertTrue(propertyValues.contains("prefix"));
+        assertTrue(propertyValues.contains("suffix"));
+
+        // process factory
+        PropertiesMetadataProvider metadataProvider = new PropertiesMetadataProvider();
+        MetadataManager metadataManager = new SimpleMetadataManager(Collections.singleton(metadataProvider));
+        NamedConstructorArgs namedConstructorArgs = new NamedConstructorArgs(metadataManager);
+        namedConstructorArgs.postProcessBeanFactory(factory);
+
+        // post conditions
+        beanDefinition = (RootBeanDefinition) factory.getBeanDefinition(beanName);
+        assertNotNull(beanDefinition);
+
+        constructorArgumentValues = beanDefinition.getConstructorArgumentValues();
+        assertEquals(2, constructorArgumentValues.getArgumentCount());
+        assertTrue(constructorArgumentValues.getGenericArgumentValues().isEmpty());
+        ConstructorArgumentValues.ValueHolder arg = constructorArgumentValues.getIndexedArgumentValue(0, String.class);
+        assertNotNull(arg);
+        assertEquals("I'm sorry ", arg.getValue());
+        arg = constructorArgumentValues.getIndexedArgumentValue(1, String.class);
+        assertNotNull(arg);
+        assertEquals("; I'm afraid I can't do that.", arg.getValue());
+
+        propertyValues = beanDefinition.getPropertyValues();
+        assertFalse(propertyValues.contains("prefix"));
+        assertFalse(propertyValues.contains("suffix"));
+    }
+
+    public void testForcedConstructor() {
+        // pre conditions
+        String beanName = "hal";
+        assertTrue(factory.containsBeanDefinition(beanName));
+        RootBeanDefinition beanDefinition = (RootBeanDefinition) factory.getBeanDefinition(beanName);
+        assertNotNull(beanDefinition);
+
+        ConstructorArgumentValues constructorArgumentValues = beanDefinition.getConstructorArgumentValues();
+        assertEquals(0, constructorArgumentValues.getArgumentCount());
+
+        MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
+        assertTrue(propertyValues.contains("prefix"));
+        assertTrue(propertyValues.contains("suffix"));
+
+        // process factory
+        PropertiesMetadataProvider metadataProvider = new PropertiesMetadataProvider() {
+            public void addClassMetadata(ClassMetadata classMetadata) {
+                super.addClassMetadata(classMetadata);
+                if (classMetadata.getType().equals(HelloMessage.class)) {
+                    ConstructorMetadata constructor = classMetadata.getConstructor(
+                            new ConstructorSignature(new String[] {"java.lang.String", "java.lang.String", "java.util.Properties"}));
+                    constructor.put("always-use", null);
+                }
+            }
+        };
+
+        MetadataManager metadataManager = new SimpleMetadataManager(Collections.singleton(metadataProvider));
+        NamedConstructorArgs namedConstructorArgs = new NamedConstructorArgs(metadataManager);
+        namedConstructorArgs.postProcessBeanFactory(factory);
+
+        // post conditions
+        beanDefinition = (RootBeanDefinition) factory.getBeanDefinition(beanName);
+        assertNotNull(beanDefinition);
+
+        constructorArgumentValues = beanDefinition.getConstructorArgumentValues();
+        assertEquals(3, constructorArgumentValues.getArgumentCount());
+        assertTrue(constructorArgumentValues.getGenericArgumentValues().isEmpty());
+        ConstructorArgumentValues.ValueHolder arg = constructorArgumentValues.getIndexedArgumentValue(0, String.class);
+        assertNotNull(arg);
+        assertEquals("I'm sorry ", arg.getValue());
+        arg = constructorArgumentValues.getIndexedArgumentValue(1, String.class);
+        assertNotNull(arg);
+        assertEquals("; I'm afraid I can't do that.", arg.getValue());
+        arg = constructorArgumentValues.getIndexedArgumentValue(2, Properties.class);
+        assertNotNull(arg);
+        assertNull(arg.getValue());
+
+        propertyValues = beanDefinition.getPropertyValues();
+        assertFalse(propertyValues.contains("prefix"));
+        assertFalse(propertyValues.contains("suffix"));
+    }
+
+    protected void setUp() throws Exception {
+        factory = new DefaultListableBeanFactory();
+        Resource resource = new FileSystemResource(new File("src/test-data/NamedConstructorArgsTest.xml"));
+        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
+        reader.loadBeanDefinitions(resource);
+    }
+}
diff --git a/kernel/src/test/org/gbean/spring/ObjectNameBuilderTest.java b/kernel/src/test/org/gbean/spring/ObjectNameBuilderTest.java
new file mode 100644
index 0000000..fcdb1a9
--- /dev/null
+++ b/kernel/src/test/org/gbean/spring/ObjectNameBuilderTest.java
@@ -0,0 +1,91 @@
+/**
+ *
+ * Copyright 2005 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.gbean.spring;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import javax.management.ObjectName;
+
+import junit.framework.TestCase;
+import org.gbean.kernel.ServiceName;
+import org.gbean.metadata.MetadataManager;
+import org.gbean.metadata.simple.PropertiesMetadataProvider;
+import org.gbean.metadata.simple.SimpleMetadataManager;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.Resource;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class ObjectNameBuilderTest extends TestCase {
+    private DefaultListableBeanFactory factory;
+
+    public void testObjectNameProperty() {
+        String beanName = "hal";
+        assertTrue(factory.containsBeanDefinition(beanName));
+        BeanDefinition beanDefinition = factory.getBeanDefinition(beanName);
+        assertNotNull(beanDefinition);
+        assertTrue(beanDefinition.getPropertyValues().contains("gbean-object-name"));
+
+        ObjectName objectName = ServiceName.createName((String) beanDefinition.getPropertyValues().getPropertyValue("gbean-object-name").getValue());
+
+        // convert properties into named constructor args
+        List metadataProviders = new ArrayList(2);
+        metadataProviders.add(new PropertiesMetadataProvider());
+        MetadataManager metadataManager = new SimpleMetadataManager(metadataProviders);
+
+        ObjectNameBuilder objectNameBuilder = new ObjectNameBuilder(metadataManager, "domain", "server", "application");
+        objectNameBuilder.postProcessBeanFactory(factory);
+
+        assertFalse(beanDefinition.getPropertyValues().contains("gbean-object-name"));
+        assertEquals(objectName, objectNameBuilder.getObjectName(beanName));
+    }
+
+    public void testObjectNameCreation() {
+        String beanName = "foobar";
+        assertTrue(factory.containsBeanDefinition(beanName));
+        BeanDefinition beanDefinition = factory.getBeanDefinition(beanName);
+        assertNotNull(beanDefinition);
+        assertFalse(beanDefinition.getPropertyValues().contains("gbean-object-name"));
+
+        // convert properties into named constructor args
+        List metadataProviders = new ArrayList(2);
+        metadataProviders.add(new PropertiesMetadataProvider());
+        MetadataManager metadataManager = new SimpleMetadataManager(metadataProviders);
+
+        ObjectNameBuilder objectNameBuilder = new ObjectNameBuilder(metadataManager, "domain", "server", "module");
+        objectNameBuilder.postProcessBeanFactory(factory);
+
+        ObjectName objectName = ServiceName.createName("domain:J2EEServer=server,J2EEApplication=null,J2EEModule=module,name=" + beanName);
+
+        objectNameBuilder.postProcessBeanFactory(factory);
+
+        assertFalse(beanDefinition.getPropertyValues().contains("gbean-object-name"));
+        assertEquals(objectName, objectNameBuilder.getObjectName(beanName));
+    }
+
+    protected void setUp() throws Exception {
+        factory = new DefaultListableBeanFactory();
+        Resource resource = new FileSystemResource(new File("src/test-data/PostProcessorTest.xml"));
+        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
+        reader.loadBeanDefinitions(resource);
+    }
+}