http://git-wip-us.apache.org/repos/asf/karaf/blob/85c65dd6/jndi/src/main/java/org/apache/karaf/jndi/command/AliasCommand.java
----------------------------------------------------------------------
diff --git a/jndi/src/main/java/org/apache/karaf/jndi/command/AliasCommand.java 
b/jndi/src/main/java/org/apache/karaf/jndi/command/AliasCommand.java
new file mode 100644
index 0000000..08e3912
--- /dev/null
+++ b/jndi/src/main/java/org/apache/karaf/jndi/command/AliasCommand.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.jndi.command;
+
+import org.apache.karaf.jndi.JndiService;
+import org.apache.karaf.jndi.command.completers.ContextsCompleter;
+import org.apache.karaf.jndi.command.completers.NamesCompleter;
+import org.apache.karaf.shell.api.action.Action;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Reference;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+@Command(scope = "jndi", name = "alias", description = "Create a JNDI alias on 
a given name.")
+@Service
+public class AliasCommand implements Action {
+
+    @Argument(index = 0, name = "name", description = "The JNDI name", 
required = true, multiValued = false)
+    @Completion(NamesCompleter.class)
+    String name;
+
+    @Argument(index = 1, name = "alias", description = "The JNDI alias", 
required = true, multiValued = false)
+    @Completion(ContextsCompleter.class)
+    String alias;
+
+    @Reference
+    JndiService jndiService;
+
+    @Override
+    public Object execute() throws Exception {
+        jndiService.alias(name, alias);
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/85c65dd6/jndi/src/main/java/org/apache/karaf/jndi/command/BindCommand.java
----------------------------------------------------------------------
diff --git a/jndi/src/main/java/org/apache/karaf/jndi/command/BindCommand.java 
b/jndi/src/main/java/org/apache/karaf/jndi/command/BindCommand.java
new file mode 100644
index 0000000..b181762
--- /dev/null
+++ b/jndi/src/main/java/org/apache/karaf/jndi/command/BindCommand.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.jndi.command;
+
+import org.apache.karaf.jndi.JndiService;
+import org.apache.karaf.jndi.command.completers.ContextsCompleter;
+import org.apache.karaf.jndi.command.completers.ServicesIdCompleter;
+import org.apache.karaf.shell.api.action.Action;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Reference;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+@Command(scope = "jndi", name = "bind", description = "Bind an OSGi service in 
the JNDI context")
+@Service
+public class BindCommand implements Action {
+
+    @Argument(index = 0, name = "service", description = "The ID of the OSGi 
service to bind", required = true, multiValued = false)
+    @Completion(ServicesIdCompleter.class)
+    Long serviceId;
+
+    @Argument(index = 1, name = "name", description = "The JNDI name to bind 
the OSGi service", required = true, multiValued = false)
+    @Completion(ContextsCompleter.class)
+    String name;
+
+    @Reference
+    JndiService jndiService;
+
+    @Override
+    public Object execute() throws Exception {
+        jndiService.bind(serviceId, name);
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/85c65dd6/jndi/src/main/java/org/apache/karaf/jndi/command/ContextsCommand.java
----------------------------------------------------------------------
diff --git 
a/jndi/src/main/java/org/apache/karaf/jndi/command/ContextsCommand.java 
b/jndi/src/main/java/org/apache/karaf/jndi/command/ContextsCommand.java
new file mode 100644
index 0000000..2209479
--- /dev/null
+++ b/jndi/src/main/java/org/apache/karaf/jndi/command/ContextsCommand.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.jndi.command;
+
+import org.apache.karaf.jndi.JndiService;
+import org.apache.karaf.jndi.command.completers.ContextsCompleter;
+import org.apache.karaf.shell.api.action.Action;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Reference;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.apache.karaf.shell.support.table.ShellTable;
+
+import java.util.List;
+
+@Command(scope = "jndi", name = "contexts", description = "List the JNDI 
sub-contexts.")
+@Service
+public class ContextsCommand implements Action {
+
+    @Argument(index = 0, name = "context", description = "The base JNDI 
context", required = false, multiValued = false)
+    @Completion(ContextsCompleter.class)
+    String context;
+
+    @Reference
+    JndiService jndiService;
+
+    @Override
+    public Object execute() throws Exception {
+        ShellTable table = new ShellTable();
+
+        table.column("JNDI Sub-Context");
+
+        List<String> contexts;
+        if (context == null) {
+            contexts = jndiService.contexts();
+        } else {
+            contexts = jndiService.contexts(context);
+        }
+
+        for (String c : contexts) {
+            table.addRow().addContent(c);
+        }
+
+        table.print(System.out);
+
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/85c65dd6/jndi/src/main/java/org/apache/karaf/jndi/command/CreateCommand.java
----------------------------------------------------------------------
diff --git 
a/jndi/src/main/java/org/apache/karaf/jndi/command/CreateCommand.java 
b/jndi/src/main/java/org/apache/karaf/jndi/command/CreateCommand.java
new file mode 100644
index 0000000..86663bf
--- /dev/null
+++ b/jndi/src/main/java/org/apache/karaf/jndi/command/CreateCommand.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.jndi.command;
+
+import org.apache.karaf.jndi.JndiService;
+import org.apache.karaf.jndi.command.completers.ContextsCompleter;
+import org.apache.karaf.shell.api.action.Action;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Reference;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+@Command(scope = "jndi",  name = "create", description = "Create a new JNDI 
sub-context.")
+@Service
+public class CreateCommand implements Action {
+
+    @Argument(index = 0, name = "context", description = "The JNDI sub-context 
name", required = true, multiValued = false)
+    @Completion(ContextsCompleter.class)
+    String context;
+
+    @Reference
+    JndiService jndiService;
+
+    @Override
+    public Object execute() throws Exception {
+        jndiService.create(context);
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/85c65dd6/jndi/src/main/java/org/apache/karaf/jndi/command/DeleteCommand.java
----------------------------------------------------------------------
diff --git 
a/jndi/src/main/java/org/apache/karaf/jndi/command/DeleteCommand.java 
b/jndi/src/main/java/org/apache/karaf/jndi/command/DeleteCommand.java
new file mode 100644
index 0000000..3cd3a40
--- /dev/null
+++ b/jndi/src/main/java/org/apache/karaf/jndi/command/DeleteCommand.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.jndi.command;
+
+import org.apache.karaf.jndi.JndiService;
+import org.apache.karaf.jndi.command.completers.ContextsCompleter;
+import org.apache.karaf.shell.api.action.Action;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Reference;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+@Command(scope = "jndi", name = "delete", description = "Delete a JNDI 
sub-context.")
+@Service
+public class DeleteCommand implements Action {
+
+    @Argument(index = 0, name = "context", description = "The JNDI sub-context 
name", required = true, multiValued = false)
+    @Completion(ContextsCompleter.class)
+    String context;
+
+    @Reference
+    JndiService jndiService;
+
+    @Override
+    public Object execute() throws Exception {
+        jndiService.delete(context);
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/85c65dd6/jndi/src/main/java/org/apache/karaf/jndi/command/NamesCommand.java
----------------------------------------------------------------------
diff --git a/jndi/src/main/java/org/apache/karaf/jndi/command/NamesCommand.java 
b/jndi/src/main/java/org/apache/karaf/jndi/command/NamesCommand.java
new file mode 100644
index 0000000..0345f11
--- /dev/null
+++ b/jndi/src/main/java/org/apache/karaf/jndi/command/NamesCommand.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.jndi.command;
+
+import org.apache.karaf.jndi.JndiService;
+import org.apache.karaf.jndi.command.completers.ContextsCompleter;
+import org.apache.karaf.shell.api.action.Action;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Reference;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.apache.karaf.shell.support.table.ShellTable;
+
+import java.util.Map;
+
+@Command(scope = "jndi", name = "names", description = "List the JNDI names.")
+@Service
+public class NamesCommand implements Action {
+
+    @Argument(index = 0, name = "context", description = "The JNDI context to 
display the names", required = false, multiValued = false)
+    @Completion(ContextsCompleter.class)
+    String context;
+
+    @Reference
+    JndiService jndiService;
+
+    @Override
+    public Object execute() throws Exception {
+        ShellTable table = new ShellTable();
+
+        table.column("JNDI Name");
+        table.column("Class Name");
+
+        Map<String, String> names;
+        if (context == null) {
+            names = jndiService.names();
+        } else {
+            names = jndiService.names(context);
+        }
+
+        for (String name : names.keySet()) {
+            table.addRow().addContent(name, names.get(name));
+        }
+
+        table.print(System.out);
+
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/85c65dd6/jndi/src/main/java/org/apache/karaf/jndi/command/UnbindCommand.java
----------------------------------------------------------------------
diff --git 
a/jndi/src/main/java/org/apache/karaf/jndi/command/UnbindCommand.java 
b/jndi/src/main/java/org/apache/karaf/jndi/command/UnbindCommand.java
new file mode 100644
index 0000000..b93c7b9
--- /dev/null
+++ b/jndi/src/main/java/org/apache/karaf/jndi/command/UnbindCommand.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.jndi.command;
+
+import org.apache.karaf.jndi.JndiService;
+import org.apache.karaf.jndi.command.completers.NamesCompleter;
+import org.apache.karaf.shell.api.action.Action;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Reference;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+@Command(scope = "jndi", name = "unbind", description = "Unbind a JNDI name.")
+@Service
+public class UnbindCommand implements Action {
+
+    @Argument(index = 0, name = "name", description = "The JNDI name to 
unbind", required = true, multiValued = false)
+    @Completion(NamesCompleter.class)
+    String name;
+
+    @Reference
+    JndiService jndiService;
+
+    @Override
+    public Object execute() throws Exception {
+        jndiService.unbind(name);
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/85c65dd6/jndi/src/main/java/org/apache/karaf/jndi/command/completers/ContextsCompleter.java
----------------------------------------------------------------------
diff --git 
a/jndi/src/main/java/org/apache/karaf/jndi/command/completers/ContextsCompleter.java
 
b/jndi/src/main/java/org/apache/karaf/jndi/command/completers/ContextsCompleter.java
new file mode 100644
index 0000000..8f2ac29
--- /dev/null
+++ 
b/jndi/src/main/java/org/apache/karaf/jndi/command/completers/ContextsCompleter.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.jndi.command.completers;
+
+import org.apache.karaf.jndi.JndiService;
+import org.apache.karaf.shell.api.action.lifecycle.Reference;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.apache.karaf.shell.api.console.CommandLine;
+import org.apache.karaf.shell.api.console.Completer;
+import org.apache.karaf.shell.api.console.Session;
+import org.apache.karaf.shell.support.completers.StringsCompleter;
+
+import java.util.List;
+
+/**
+ * Completers on the JNDI contexts.
+ */
+@Service
+public class ContextsCompleter implements Completer {
+
+    @Reference
+    private JndiService jndiService;
+
+    @Override
+    public int complete(Session session, CommandLine commandLine, List<String> 
candidates) {
+        StringsCompleter delegate = new StringsCompleter();
+        try {
+            List<String> contexts = jndiService.contexts();
+            for (String context : contexts) {
+                delegate.getStrings().add(context);
+            }
+        } catch (Exception e) {
+            // nothing to do
+        }
+        return delegate.complete(session, commandLine, candidates);
+    }
+
+    public JndiService getJndiService() {
+        return jndiService;
+    }
+
+    public void setJndiService(JndiService jndiService) {
+        this.jndiService = jndiService;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/85c65dd6/jndi/src/main/java/org/apache/karaf/jndi/command/completers/NamesCompleter.java
----------------------------------------------------------------------
diff --git 
a/jndi/src/main/java/org/apache/karaf/jndi/command/completers/NamesCompleter.java
 
b/jndi/src/main/java/org/apache/karaf/jndi/command/completers/NamesCompleter.java
new file mode 100644
index 0000000..d94e3f4
--- /dev/null
+++ 
b/jndi/src/main/java/org/apache/karaf/jndi/command/completers/NamesCompleter.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.jndi.command.completers;
+
+import org.apache.karaf.jndi.JndiService;
+import org.apache.karaf.shell.api.action.lifecycle.Reference;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.apache.karaf.shell.api.console.CommandLine;
+import org.apache.karaf.shell.api.console.Completer;
+import org.apache.karaf.shell.api.console.Session;
+import org.apache.karaf.shell.support.completers.StringsCompleter;
+
+import java.util.List;
+
+/**
+ * Completer to the JNDI names.
+ */
+@Service
+public class NamesCompleter implements Completer {
+
+    @Reference
+    private JndiService jndiService;
+
+    @Override
+    public int complete(Session session, CommandLine commandLine, List<String> 
candidates) {
+        StringsCompleter delegate = new StringsCompleter();
+        try {
+            for (String name : jndiService.names().keySet()) {
+                delegate.getStrings().add(name);
+            }
+        } catch (Exception e) {
+            // nothing to do
+        }
+        return delegate.complete(session, commandLine, candidates);
+    }
+
+    public JndiService getJndiService() {
+        return jndiService;
+    }
+
+    public void setJndiService(JndiService jndiService) {
+        this.jndiService = jndiService;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/85c65dd6/jndi/src/main/java/org/apache/karaf/jndi/command/completers/ServicesIdCompleter.java
----------------------------------------------------------------------
diff --git 
a/jndi/src/main/java/org/apache/karaf/jndi/command/completers/ServicesIdCompleter.java
 
b/jndi/src/main/java/org/apache/karaf/jndi/command/completers/ServicesIdCompleter.java
new file mode 100644
index 0000000..c7cdb80
--- /dev/null
+++ 
b/jndi/src/main/java/org/apache/karaf/jndi/command/completers/ServicesIdCompleter.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.jndi.command.completers;
+
+import org.apache.karaf.shell.api.action.lifecycle.Reference;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.apache.karaf.shell.api.console.CommandLine;
+import org.apache.karaf.shell.api.console.Completer;
+import org.apache.karaf.shell.api.console.Session;
+import org.apache.karaf.shell.support.completers.StringsCompleter;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+
+import java.util.List;
+
+/**
+ * Completer on the OSGi services ID.
+ */
+@Service
+public class ServicesIdCompleter implements Completer {
+
+    @Reference
+    private BundleContext bundleContext;
+
+    @Override
+    public int complete(Session session, CommandLine commandLine, List<String> 
candidates) {
+        StringsCompleter delegate = new StringsCompleter();
+        Bundle[] bundles = bundleContext.getBundles();
+        for (Bundle bundle : bundles) {
+            ServiceReference[] references = bundle.getRegisteredServices();
+            if (references != null) {
+                for (ServiceReference reference : references) {
+                    if (reference.getProperty(Constants.SERVICE_ID) != null) {
+                        
delegate.getStrings().add(reference.getProperty(Constants.SERVICE_ID).toString());
+                    }
+                }
+            }
+        }
+        return delegate.complete(session, commandLine, candidates);
+    }
+
+    public BundleContext getBundleContext() {
+        return bundleContext;
+    }
+
+    public void setBundleContext(BundleContext bundleContext) {
+        this.bundleContext = bundleContext;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/85c65dd6/jndi/src/main/java/org/apache/karaf/jndi/internal/JndiMBeanImpl.java
----------------------------------------------------------------------
diff --git 
a/jndi/src/main/java/org/apache/karaf/jndi/internal/JndiMBeanImpl.java 
b/jndi/src/main/java/org/apache/karaf/jndi/internal/JndiMBeanImpl.java
new file mode 100644
index 0000000..2433618
--- /dev/null
+++ b/jndi/src/main/java/org/apache/karaf/jndi/internal/JndiMBeanImpl.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.jndi.internal;
+
+import org.apache.karaf.jndi.JndiService;
+import org.apache.karaf.jndi.JndiMBean;
+
+import javax.management.MBeanException;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Implementation of the JndiMBean
+ */
+public class JndiMBeanImpl implements JndiMBean {
+
+    private JndiService jndiService;
+
+    @Override
+    public Map<String, String> getNames() throws MBeanException {
+        try {
+            return this.jndiService.names();
+        } catch (Throwable t) {
+            throw new MBeanException(null, t.getMessage());
+        }
+    }
+
+    @Override
+    public List<String> getContexts() throws MBeanException {
+        try {
+            return this.jndiService.contexts();
+        } catch (Throwable t) {
+            throw new MBeanException(null, t.getMessage());
+        }
+    }
+
+    @Override
+    public Map<String, String> getNames(String context) throws MBeanException {
+        try {
+            return this.jndiService.names(context);
+        } catch (Throwable t) {
+            throw new MBeanException(null, t.getMessage());
+        }
+    }
+
+    @Override
+    public List<String> getContexts(String context) throws MBeanException {
+        try {
+            return this.jndiService.contexts(context);
+        } catch (Throwable t) {
+            throw new MBeanException(null, t.getMessage());
+        }
+    }
+
+    @Override
+    public void alias(String name, String alias) throws MBeanException {
+        try {
+            this.jndiService.alias(name, alias);
+        } catch (Throwable t) {
+            throw new MBeanException(null, t.getMessage());
+        }
+    }
+
+    @Override
+    public void bind(Long serviceId, String name) throws MBeanException {
+        try {
+            this.jndiService.bind(serviceId, name);
+        } catch (Throwable t) {
+            throw new MBeanException(null, t.getMessage());
+        }
+    }
+
+    @Override
+    public void unbind(String name) throws MBeanException {
+        try {
+            this.jndiService.unbind(name);
+        } catch (Throwable t) {
+            throw new MBeanException(null, t.getMessage());
+        }
+    }
+
+    @Override
+    public void create(String context) throws MBeanException {
+        try {
+            this.jndiService.create(context);
+        } catch (Throwable t) {
+            throw new MBeanException(null, t.getMessage());
+        }
+    }
+
+    @Override
+    public void delete(String context) throws MBeanException {
+        try {
+            this.jndiService.delete(context);
+        } catch (Throwable t) {
+            throw new MBeanException(null, t.getMessage());
+        }
+    }
+
+    public JndiService getJndiService() {
+        return jndiService;
+    }
+
+    public void setJndiService(JndiService jndiService) {
+        this.jndiService = jndiService;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/85c65dd6/jndi/src/main/java/org/apache/karaf/jndi/internal/JndiServiceImpl.java
----------------------------------------------------------------------
diff --git 
a/jndi/src/main/java/org/apache/karaf/jndi/internal/JndiServiceImpl.java 
b/jndi/src/main/java/org/apache/karaf/jndi/internal/JndiServiceImpl.java
new file mode 100644
index 0000000..68d6b94
--- /dev/null
+++ b/jndi/src/main/java/org/apache/karaf/jndi/internal/JndiServiceImpl.java
@@ -0,0 +1,313 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.jndi.internal;
+
+import org.apache.aries.proxy.ProxyManager;
+import org.apache.karaf.jndi.JndiService;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+
+import javax.naming.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Implementation of the JNDI Service.
+ */
+public class JndiServiceImpl implements JndiService {
+
+    private BundleContext bundleContext;
+    private ProxyManager proxyManager;
+
+    private final static String OSGI_JNDI_CONTEXT_PREFIX = "osgi:service/";
+    private final static String OSGI_JNDI_SERVICE_PROPERTY = 
"osgi.jndi.service.name";
+
+    @Override
+    public Map<String, String> names() throws Exception {
+        Map<String, String> result = names("/");
+        result.putAll(names(OSGI_JNDI_CONTEXT_PREFIX));
+        return result;
+    }
+
+    @Override
+    public Map<String, String> names(String name) throws Exception {
+        Map<String, String> map = new HashMap<String, String>();
+        if (name.startsWith(OSGI_JNDI_CONTEXT_PREFIX)) {
+            // OSGi service binding
+            // make a lookup using directly the OSGi service
+            Bundle[] bundles = bundleContext.getBundles();
+            for (Bundle bundle : bundles) {
+                ServiceReference<?>[] services = 
bundle.getRegisteredServices();
+                if (services != null) {
+                    for (ServiceReference service : services) {
+                        if (service.getProperty(OSGI_JNDI_SERVICE_PROPERTY) != 
null) {
+                            Object actualService = 
bundleContext.getService(service);
+                            if (proxyManager.isProxy(actualService)) {
+                                actualService = 
proxyManager.unwrap(actualService).call();
+                            }
+                            map.put(OSGI_JNDI_CONTEXT_PREFIX + 
service.getProperty(OSGI_JNDI_SERVICE_PROPERTY), 
actualService.getClass().getName());
+                            bundleContext.ungetService(service);
+                        }
+                    }
+                }
+            }
+        } else {
+            // "real" JNDI lookup
+            Context context = new InitialContext();
+            NamingEnumeration<NameClassPair> pairs = context.list(name);
+            while (pairs.hasMoreElements()) {
+                NameClassPair pair = pairs.nextElement();
+                Object o;
+                if (name != null) {
+                    o = context.lookup(name + "/" + pair.getName());
+                } else {
+                    o = context.lookup(pair.getName());
+                }
+                if (o instanceof Context) {
+                    StringBuilder sb = new StringBuilder();
+                    sb.append("/" + pair.getName());
+                    names((Context) o, sb, map);
+                } else {
+                    map.put("/" + pair.getName(), pair.getClassName());
+                }
+            }
+        }
+        return map;
+    }
+
+    public List<String> contexts() throws Exception {
+        return contexts("/");
+    }
+
+    public List<String> contexts(String name) throws Exception {
+        List<String> contexts = new ArrayList<String>();
+        Context context = new InitialContext();
+        NamingEnumeration<NameClassPair> pairs = context.list(name);
+        while (pairs.hasMoreElements()) {
+            NameClassPair pair = pairs.nextElement();
+            Object o;
+            if (name != null) {
+                o = context.lookup(name + "/" + pair.getName());
+            } else {
+                o = context.lookup(pair.getName());
+            }
+            if (o instanceof Context) {
+                StringBuilder sb = new StringBuilder();
+                sb.append("/" + pair.getName());
+                contexts((Context) o, sb, contexts);
+            }
+        }
+        return contexts;
+    }
+
+    private void contexts(Context context, StringBuilder sb, List<String> 
contexts) throws Exception {
+        NamingEnumeration list = context.listBindings("");
+        while (list.hasMore()) {
+            Binding item = (Binding) list.next();
+            String name = item.getName();
+            Object o = item.getObject();
+            if (o instanceof Context) {
+                if (((Context) o).list("").hasMoreElements()) {
+                    sb.append("/").append(name);
+                    contexts((Context) o, sb, contexts);
+                } else {
+                    contexts.add(sb.toString() + "/" + name);
+                }
+            }
+        }
+    }
+
+    /**
+     * Recursively list a context/names
+     *
+     * @param ctx the startup context.
+     * @param sb the string builder where to construct the full qualified name.
+     * @param map the final map containing name/class name pairs.
+     * @throws Exception
+     */
+    private static final void names(Context ctx, StringBuilder sb, Map<String, 
String> map) throws Exception {
+        NamingEnumeration list = ctx.listBindings("");
+        while (list.hasMore()) {
+            Binding item = (Binding) list.next();
+            String className = item.getClassName();
+            String name = item.getName();
+            Object o = item.getObject();
+            if (o instanceof Context) {
+                sb.append("/").append(name);
+                names((Context) o, sb, map);
+            } else {
+                map.put(sb.toString() + "/" + name, className);
+            }
+        }
+    }
+
+    @Override
+    public void create(String name) throws Exception {
+        Context context = new InitialContext();
+        String[] splitted = name.split("/");
+        if (splitted.length > 0) {
+            for (int i = 0; i < splitted.length; i++) {
+                try {
+                    Object o = context.lookup(splitted[i]);
+                    if (!(o instanceof Context)) {
+                        throw new NamingException("Name " + splitted[i] + " 
already exists");
+                    }
+                } catch (NameNotFoundException e) {
+                    context.createSubcontext(splitted[i]);
+                }
+                context = (Context) context.lookup(splitted[i]);
+            }
+        } else {
+            context.createSubcontext(name);
+        }
+    }
+
+    @Override
+    public void delete(String name) throws Exception {
+        Context context = new InitialContext();
+        context.destroySubcontext(name);
+    }
+
+    @Override
+    public void bind(long serviceId, String name) throws Exception {
+        Context context = new InitialContext();
+        Bundle[] bundles = bundleContext.getBundles();
+        for (Bundle bundle : bundles) {
+            ServiceReference<?>[] services = bundle.getRegisteredServices();
+            if (services != null) {
+                for (ServiceReference service : services) {
+                    if (service.getProperty(Constants.SERVICE_ID) != null && 
((Long) service.getProperty(Constants.SERVICE_ID)) == serviceId) {
+                        Object actualService = 
bundleContext.getService(service);
+                        if (proxyManager.isProxy(actualService)) {
+                            actualService = 
proxyManager.unwrap(actualService).call();
+                        }
+                        try {
+                            String[] splitted = name.split("/");
+                            if (splitted.length > 0) {
+                                for (int i = 0; i < splitted.length - 1; i++) {
+                                    try {
+                                        Object o = context.lookup(splitted[i]);
+                                        if (!(o instanceof Context)) {
+                                            throw new NamingException("Name " 
+ splitted[i] + " already exists");
+                                        }
+                                    } catch (NameNotFoundException nnfe) {
+                                        context.createSubcontext(splitted[i]);
+                                    }
+                                    context = (Context) 
context.lookup(splitted[i]);
+                                }
+                                name = splitted[splitted.length - 1];
+                            }
+                            context.bind(name, actualService);
+                        } finally {
+                            bundleContext.ungetService(service);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public void alias(String name, String alias) throws Exception {
+        Context context = new InitialContext();
+        if (name.startsWith(OSGI_JNDI_CONTEXT_PREFIX)) {
+            // get the object
+            Bundle[] bundles = bundleContext.getBundles();
+            for (Bundle bundle : bundles) {
+                ServiceReference<?>[] services = 
bundle.getRegisteredServices();
+                if (services != null) {
+                    for (ServiceReference service : services) {
+                        if (service.getProperty(OSGI_JNDI_SERVICE_PROPERTY) != 
null && ((String) 
service.getProperty(OSGI_JNDI_SERVICE_PROPERTY)).equals(name.substring(OSGI_JNDI_CONTEXT_PREFIX.length())))
 {
+                            Object actualService = 
bundleContext.getService(service);
+                            try {
+                                if (proxyManager.isProxy(actualService)) {
+                                    actualService = 
proxyManager.unwrap(actualService).call();
+                                }
+                                String[] splitted = alias.split("/");
+                                if (splitted.length > 0) {
+                                    for (int i = 0; i < splitted.length - 1; 
i++) {
+                                        try {
+                                            Object o = 
context.lookup(splitted[i]);
+                                            if (!(o instanceof Context)) {
+                                                throw new 
NamingException("Name " + splitted[i] + " already exists");
+                                            }
+                                        } catch (NameNotFoundException nnfe) {
+                                            
context.createSubcontext(splitted[i]);
+                                        }
+                                        context = (Context) 
context.lookup(splitted[i]);
+                                    }
+                                    alias = splitted[splitted.length -1];
+                                }
+                                context.bind(alias, actualService);
+                            } finally {
+                                bundleContext.ungetService(service);
+                            }
+                        }
+                    }
+                }
+            }
+        } else {
+            Object object = context.lookup(name);
+            String[] splitted = alias.split("/");
+            if (splitted.length > 0) {
+                for (int i = 0; i < splitted.length - 1; i++) {
+                    try {
+                        Object o = context.lookup(splitted[i]);
+                        if (!(o instanceof Context)) {
+                            throw new NamingException("Name " + splitted[i] + 
" already exists");
+                        }
+                    } catch (NameNotFoundException nnfe) {
+                        context.createSubcontext(splitted[i]);
+                    }
+                    context = (Context) context.lookup(splitted[i]);
+                }
+                alias = splitted[splitted.length - 1];
+            }
+            context.bind(alias, object);
+        }
+    }
+
+    @Override
+    public void unbind(String name) throws Exception {
+        InitialContext context = new InitialContext();
+        if (name.startsWith(OSGI_JNDI_CONTEXT_PREFIX)) {
+            throw new IllegalArgumentException("You can't unbind a name from 
the " + OSGI_JNDI_CONTEXT_PREFIX + " JNDI context.");
+        }
+        context.unbind(name);
+    }
+
+    public BundleContext getBundleContext() {
+        return bundleContext;
+    }
+
+    public void setBundleContext(BundleContext bundleContext) {
+        this.bundleContext = bundleContext;
+    }
+
+    public ProxyManager getProxyManager() {
+        return proxyManager;
+    }
+
+    public void setProxyManager(ProxyManager proxyManager) {
+        this.proxyManager = proxyManager;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/85c65dd6/jndi/src/main/resources/OSGI-INF/blueprint/jndi-core.xml
----------------------------------------------------------------------
diff --git a/jndi/src/main/resources/OSGI-INF/blueprint/jndi-core.xml 
b/jndi/src/main/resources/OSGI-INF/blueprint/jndi-core.xml
new file mode 100644
index 0000000..e6be3b1
--- /dev/null
+++ b/jndi/src/main/resources/OSGI-INF/blueprint/jndi-core.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+        Licensed to the Apache Software Foundation (ASF) under one or more
+        contributor license agreements. See the NOTICE file distributed with
+        this work for additional information regarding copyright ownership.
+        The ASF licenses this file to You under the Apache License, Version
+        2.0 (the "License"); you may not use this file except in compliance
+        with the License. You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0 Unless required by
+        applicable law or agreed to in writing, software distributed under the
+        License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+        CONDITIONS OF ANY KIND, either express or implied. See the License for
+        the specific language governing permissions and limitations under the
+        License.
+    -->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0";
+           
xmlns:ext="http://aries.apache.org/blueprint/xmlns/blueprint-ext/v1.0.0";
+           default-activation="lazy">
+
+    <ext:property-placeholder placeholder-prefix="$[" placeholder-suffix="]"/>
+
+    <bean id="karafInitialContextFactory" 
class="org.apache.karaf.jndi.KarafInitialContextFactory"/>
+
+    <reference id="proxyManager" 
interface="org.apache.aries.proxy.ProxyManager"/>
+
+    <service ref="karafInitialContextFactory" 
interface="javax.naming.spi.InitialContextFactory"/>
+
+    <bean id="jndiService" 
class="org.apache.karaf.jndi.internal.JndiServiceImpl">
+        <property name="bundleContext" ref="blueprintBundleContext"/>
+        <property name="proxyManager" ref="proxyManager"/>
+    </bean>
+
+    <service ref="jndiService" interface="org.apache.karaf.jndi.JndiService">
+        <service-properties>
+            <!-- bind the JNDI service itself in the JNDI context -->
+            <entry key="osgi.jndi.service.name" value="jndi"/>
+        </service-properties>
+    </service>
+
+    <!-- Management -->
+    <bean id="jndiMBeanImpl" 
class="org.apache.karaf.jndi.internal.JndiMBeanImpl">
+        <property name="jndiService" ref="jndiService"/>
+    </bean>
+
+    <service ref="jndiMBeanImpl" auto-export="interfaces">
+        <service-properties>
+            <entry key="jmx.objectname" 
value="org.apache.karaf:type=jndi,name=$[karaf.name]"/>
+        </service-properties>
+    </service>
+
+</blueprint>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/karaf/blob/85c65dd6/jndi/src/main/resources/OSGI-INF/bundle.info
----------------------------------------------------------------------
diff --git a/jndi/src/main/resources/OSGI-INF/bundle.info 
b/jndi/src/main/resources/OSGI-INF/bundle.info
new file mode 100644
index 0000000..4a7a606
--- /dev/null
+++ b/jndi/src/main/resources/OSGI-INF/bundle.info
@@ -0,0 +1,19 @@
+h1. Synopsis
+
+${project.name}
+
+${project.description}
+
+Maven URL:
+[mvn:${project.groupId}/${project.artifactId}/${project.version}]
+
+h1. Description
+
+This bundle is the core implementation of the JNDI service support.
+
+JNDI allows to expose any OSGi services as JNDI names. Karaf JNDI also 
provides a set of commands and a MBean to list
+the current JNDI names, create JNDI aliases, ...
+
+h1. See also
+
+JNDI - section of the Karaf User Guide

Reply via email to