You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by jb...@apache.org on 2016/10/20 05:43:15 UTC

karaf-cellar git commit: [KARAF-4747] Add cluster KAR support

Repository: karaf-cellar
Updated Branches:
  refs/heads/master 76c1aa5f8 -> 5cf48dd71


[KARAF-4747] Add cluster KAR support


Project: http://git-wip-us.apache.org/repos/asf/karaf-cellar/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf-cellar/commit/5cf48dd7
Tree: http://git-wip-us.apache.org/repos/asf/karaf-cellar/tree/5cf48dd7
Diff: http://git-wip-us.apache.org/repos/asf/karaf-cellar/diff/5cf48dd7

Branch: refs/heads/master
Commit: 5cf48dd7183da9edfb4d20f0f5fd298961ef7e26
Parents: 76c1aa5
Author: Jean-Baptiste Onofr� <jb...@apache.org>
Authored: Thu Oct 20 07:42:38 2016 +0200
Committer: Jean-Baptiste Onofr� <jb...@apache.org>
Committed: Thu Oct 20 07:42:38 2016 +0200

----------------------------------------------------------------------
 assembly/src/main/resources/features.xml        |  9 ++
 kar/NOTICE                                      | 39 ++++++++
 kar/pom.xml                                     | 96 ++++++++++++++++++++
 .../karaf/cellar/kar/ClusterKarEvent.java       | 47 ++++++++++
 .../org/apache/karaf/cellar/kar/Constants.java  | 24 +++++
 .../karaf/cellar/kar/KarEventHandler.java       | 77 ++++++++++++++++
 .../cellar/kar/internal/osgi/Activator.java     | 81 +++++++++++++++++
 .../cellar/kar/shell/InstallKarCommand.java     | 85 +++++++++++++++++
 .../cellar/kar/shell/UninstallKarCommand.java   | 75 +++++++++++++++
 manual/src/main/asciidoc/user-guide/groups.adoc | 20 ++++
 pom.xml                                         |  6 ++
 11 files changed, 559 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/5cf48dd7/assembly/src/main/resources/features.xml
----------------------------------------------------------------------
diff --git a/assembly/src/main/resources/features.xml b/assembly/src/main/resources/features.xml
index c10087b..d9706a3 100644
--- a/assembly/src/main/resources/features.xml
+++ b/assembly/src/main/resources/features.xml
@@ -59,6 +59,14 @@
         </conditional>
     </feature>
 
+    <feature name="cellar-kar" description="Karaf kar cluster support" version="${project.version}">
+        <conditional>
+            <condition>kar</condition>
+            <feature dependency="true">cellar-hazelcast</feature>
+            <bundle>mvn:org.apache.karaf.cellar/org.apache.karaf.cellar.kar/${project.version}</bundle>
+        </conditional>
+    </feature>
+
     <feature name="cellar-bundle" description="Bundle cluster support" version="${project.version}">
         <conditional>
             <condition>bundle</condition>
@@ -81,6 +89,7 @@
         <feature>cellar-config</feature>
         <feature>cellar-bundle</feature>
         <feature>cellar-features</feature>
+        <feature>cellar-kar</feature>
         <requirement>
             karaf.cellar.provider
         </requirement>

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/5cf48dd7/kar/NOTICE
----------------------------------------------------------------------
diff --git a/kar/NOTICE b/kar/NOTICE
new file mode 100644
index 0000000..64cb235
--- /dev/null
+++ b/kar/NOTICE
@@ -0,0 +1,39 @@
+Apache Karaf Cellar
+Copyright 2011-2015 The Apache Software Foundation
+
+I. Used Software
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+The OSGi Alliance (http://www.osgi.org/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+Hazelcast (http://www.hazelcast.com/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+OPS4J (http://www.ops4j.org/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+FUSE Source (http://www.fusesource.org/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+JClouds (http://www.jclouds.org/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+SLF4J (http://www.slf4j.org/).
+Licensed under the MIT License.
+
+This product includes software from http://www.json.org.
+Copyright (c) 2002 JSON.org
+
+II. License Summary
+- Apache License 2.0
+- MIT License

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/5cf48dd7/kar/pom.xml
----------------------------------------------------------------------
diff --git a/kar/pom.xml b/kar/pom.xml
new file mode 100644
index 0000000..cda8af7
--- /dev/null
+++ b/kar/pom.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <!--
+
+        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.
+    -->
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.karaf</groupId>
+        <artifactId>cellar</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <groupId>org.apache.karaf.cellar</groupId>
+    <artifactId>org.apache.karaf.cellar.kar</artifactId>
+    <packaging>bundle</packaging>
+    <name>Apache Karaf :: Cellar :: KAR</name>
+
+    <dependencies>
+        <!-- Internal dependencies -->
+        <dependency>
+            <groupId>org.apache.karaf.cellar</groupId>
+            <artifactId>org.apache.karaf.cellar.core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.karaf.kar</groupId>
+            <artifactId>org.apache.karaf.kar.core</artifactId>
+        </dependency>
+
+        <!-- Karaf features -->
+        <dependency>
+            <groupId>org.apache.karaf.features</groupId>
+            <artifactId>org.apache.karaf.features.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.karaf.tooling</groupId>
+                <artifactId>karaf-services-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Export-Package>
+                            !org.apache.karaf.cellar.kar.internal.osgi,
+                            org.apache.karaf.cellar.kar*
+                        </Export-Package>
+                        <Import-Package>
+                            org.slf4j;version="[1.6,2)";resolution:=optional,
+                            org.apache.karaf.shell*;resolution:=optional,
+                            *
+                        </Import-Package>
+                        <Private-Package>
+                            org.apache.karaf.cellar.kar.internal.osgi,
+                            org.apache.karaf.util,
+                            org.apache.karaf.util.tracker;-split-package:=merge-first
+                        </Private-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/5cf48dd7/kar/src/main/java/org/apache/karaf/cellar/kar/ClusterKarEvent.java
----------------------------------------------------------------------
diff --git a/kar/src/main/java/org/apache/karaf/cellar/kar/ClusterKarEvent.java b/kar/src/main/java/org/apache/karaf/cellar/kar/ClusterKarEvent.java
new file mode 100644
index 0000000..e5b8e8f
--- /dev/null
+++ b/kar/src/main/java/org/apache/karaf/cellar/kar/ClusterKarEvent.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.cellar.kar;
+
+import org.apache.karaf.cellar.core.event.Event;
+import org.apache.karaf.cellar.core.event.EventType;
+
+/**
+ * Cluster kar event.
+ */
+public class ClusterKarEvent extends Event {
+
+    private String name;
+    private boolean install;
+
+    public ClusterKarEvent(String id, boolean install) {
+        super(id);
+        this.install = install;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public boolean isInstall() {
+        return install;
+    }
+
+    public void setInstall(boolean install) {
+        this.install = install;
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/5cf48dd7/kar/src/main/java/org/apache/karaf/cellar/kar/Constants.java
----------------------------------------------------------------------
diff --git a/kar/src/main/java/org/apache/karaf/cellar/kar/Constants.java b/kar/src/main/java/org/apache/karaf/cellar/kar/Constants.java
new file mode 100644
index 0000000..9668cf4
--- /dev/null
+++ b/kar/src/main/java/org/apache/karaf/cellar/kar/Constants.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.cellar.kar;
+
+/**
+ * Kar configuration constants.
+ */
+public class Constants {
+
+    // configuration category
+    public static final String CATEGORY = "kar";
+
+}

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/5cf48dd7/kar/src/main/java/org/apache/karaf/cellar/kar/KarEventHandler.java
----------------------------------------------------------------------
diff --git a/kar/src/main/java/org/apache/karaf/cellar/kar/KarEventHandler.java b/kar/src/main/java/org/apache/karaf/cellar/kar/KarEventHandler.java
new file mode 100644
index 0000000..8f3e719
--- /dev/null
+++ b/kar/src/main/java/org/apache/karaf/cellar/kar/KarEventHandler.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.cellar.kar;
+
+import org.apache.karaf.cellar.core.CellarSupport;
+import org.apache.karaf.cellar.core.Configurations;
+import org.apache.karaf.cellar.core.control.BasicSwitch;
+import org.apache.karaf.cellar.core.control.Switch;
+import org.apache.karaf.cellar.core.event.EventHandler;
+import org.apache.karaf.kar.KarService;
+import org.apache.karaf.shell.api.action.lifecycle.Reference;
+
+import java.net.URI;
+import java.util.Map;
+
+/**
+ * Handler for cluster KAR event.
+ */
+public class KarEventHandler extends CellarSupport implements EventHandler<ClusterKarEvent> {
+
+    public static final String SWITCH_ID = "org.apache.karaf.cellar.event.kar.handler";
+
+    private final Switch eventSwitch = new BasicSwitch(SWITCH_ID);
+
+    @Reference
+    private KarService karService;
+
+    @Override
+    public void handle(ClusterKarEvent event) {
+        if (event.isInstall()) {
+            try {
+                String karUrl = event.getId();
+                karService.install(new URI(karUrl));
+            } catch (Exception e) {
+                LOGGER.error("CELLAR KAR: can't install {}", event.getId(), e);
+                return;
+            }
+        } else {
+            try {
+                karService.uninstall(event.getId());
+            } catch (Exception e) {
+                LOGGER.error("CELLAR KAR: can't uninstall {}", event.getId(), e);
+                return;
+            }
+        }
+    }
+
+    @Override
+    public Class<ClusterKarEvent> getType() {
+        return ClusterKarEvent.class;
+    }
+
+    @Override
+    public Switch getSwitch() {
+        return eventSwitch;
+    }
+
+    public KarService getKarService() {
+        return karService;
+    }
+
+    public void setKarService(KarService karService) {
+        this.karService = karService;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/5cf48dd7/kar/src/main/java/org/apache/karaf/cellar/kar/internal/osgi/Activator.java
----------------------------------------------------------------------
diff --git a/kar/src/main/java/org/apache/karaf/cellar/kar/internal/osgi/Activator.java b/kar/src/main/java/org/apache/karaf/cellar/kar/internal/osgi/Activator.java
new file mode 100644
index 0000000..8a3ad4a
--- /dev/null
+++ b/kar/src/main/java/org/apache/karaf/cellar/kar/internal/osgi/Activator.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.cellar.kar.internal.osgi;
+
+import org.apache.karaf.cellar.core.ClusterManager;
+import org.apache.karaf.cellar.core.GroupManager;
+import org.apache.karaf.cellar.core.event.EventHandler;
+import org.apache.karaf.cellar.core.event.EventProducer;
+import org.apache.karaf.cellar.kar.KarEventHandler;
+import org.apache.karaf.kar.KarService;
+import org.apache.karaf.util.tracker.BaseActivator;
+import org.apache.karaf.util.tracker.annotation.ProvideService;
+import org.apache.karaf.util.tracker.annotation.RequireService;
+import org.apache.karaf.util.tracker.annotation.Services;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Hashtable;
+
+@Services(
+    provides = {
+        @ProvideService(EventHandler.class)
+    },
+    requires = {
+        @RequireService(ClusterManager.class),
+        @RequireService(GroupManager.class),
+        @RequireService(EventProducer.class),
+        @RequireService(ConfigurationAdmin.class),
+        @RequireService(KarService.class)
+    }
+)
+public class Activator extends BaseActivator {
+
+    private final static Logger LOGGER = LoggerFactory.getLogger(Activator.class);
+
+    private KarEventHandler karEventHandler;
+
+    @Override
+    public void doStart() throws Exception {
+
+        ConfigurationAdmin configurationAdmin = getTrackedService(ConfigurationAdmin.class);
+        if (configurationAdmin == null)
+            return;
+        ClusterManager clusterManager = getTrackedService(ClusterManager.class);
+        if (clusterManager == null)
+            return;
+        GroupManager groupManager = getTrackedService(GroupManager.class);
+        if (groupManager == null)
+            return;
+        EventProducer eventProducer = getTrackedService(EventProducer.class);
+        if (eventProducer == null)
+            return;
+        KarService karService = getTrackedService(KarService.class);
+        if (karService == null)
+            return;
+
+        LOGGER.debug("CELLAR KAR: init event handler");
+        karEventHandler = new KarEventHandler();
+        karEventHandler.setConfigurationAdmin(configurationAdmin);
+        karEventHandler.setClusterManager(clusterManager);
+        karEventHandler.setGroupManager(groupManager);
+        karEventHandler.setKarService(karService);
+        Hashtable props = new Hashtable();
+        props.put("managed", "true");
+        register(new Class[]{ EventHandler.class }, karEventHandler, props);
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/5cf48dd7/kar/src/main/java/org/apache/karaf/cellar/kar/shell/InstallKarCommand.java
----------------------------------------------------------------------
diff --git a/kar/src/main/java/org/apache/karaf/cellar/kar/shell/InstallKarCommand.java b/kar/src/main/java/org/apache/karaf/cellar/kar/shell/InstallKarCommand.java
new file mode 100644
index 0000000..5ae7bed
--- /dev/null
+++ b/kar/src/main/java/org/apache/karaf/cellar/kar/shell/InstallKarCommand.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.cellar.kar.shell;
+
+import org.apache.karaf.cellar.core.CellarSupport;
+import org.apache.karaf.cellar.core.Configurations;
+import org.apache.karaf.cellar.core.Group;
+import org.apache.karaf.cellar.core.control.SwitchStatus;
+import org.apache.karaf.cellar.core.event.EventProducer;
+import org.apache.karaf.cellar.core.event.EventType;
+import org.apache.karaf.cellar.core.shell.CellarCommandSupport;
+import org.apache.karaf.cellar.kar.ClusterKarEvent;
+import org.apache.karaf.cellar.kar.Constants;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Reference;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+import java.util.Map;
+
+@Command(scope = "cluster", name = "kar-install", description = "Install a KAR in a cluster group")
+@Service
+public class InstallKarCommand extends CellarCommandSupport {
+
+    @Argument(index = 0, name = "group", description = "The cluster group name", required = true, multiValued = false)
+    String groupName;
+
+    @Argument(index = 1, name = "url", description = "The URL of the KAR file to install on the cluster", required = true, multiValued = false)
+    String url;
+
+    @Reference
+    private EventProducer eventProducer;
+
+    @Override
+    protected Object doExecute() throws Exception {
+        // check if the group exists
+        Group group = groupManager.findGroupByName(groupName);
+        if (group == null) {
+            System.err.println("Cluster group " + groupName + " doesn't exist");
+            return null;
+        }
+
+        // check if the producer is ON
+        if (eventProducer.getSwitch().getStatus().equals(SwitchStatus.OFF)) {
+            System.err.println("Cluster event producer is OFF");
+            return null;
+        }
+
+        CellarSupport support = new CellarSupport();
+        support.setConfigurationAdmin(configurationAdmin);
+        support.setGroupManager(groupManager);
+        support.setClusterManager(clusterManager);
+
+        // check if the kar is allowed
+        if (support.isAllowed(group, Constants.CATEGORY, url, EventType.OUTBOUND)) {
+            // broadcast cluster event
+            ClusterKarEvent clusterEvent = new ClusterKarEvent(url, true);
+            clusterEvent.setSourceGroup(group);
+            clusterEvent.setInstall(true);
+            eventProducer.produce(clusterEvent);
+        } else {
+            System.err.println("KAR " + url + " is blocked outbound for cluster group " + groupName);
+        }
+        return null;
+    }
+
+    public EventProducer getEventProducer() {
+        return eventProducer;
+    }
+
+    public void setEventProducer(EventProducer eventProducer) {
+        this.eventProducer = eventProducer;
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/5cf48dd7/kar/src/main/java/org/apache/karaf/cellar/kar/shell/UninstallKarCommand.java
----------------------------------------------------------------------
diff --git a/kar/src/main/java/org/apache/karaf/cellar/kar/shell/UninstallKarCommand.java b/kar/src/main/java/org/apache/karaf/cellar/kar/shell/UninstallKarCommand.java
new file mode 100644
index 0000000..8dcb775
--- /dev/null
+++ b/kar/src/main/java/org/apache/karaf/cellar/kar/shell/UninstallKarCommand.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.cellar.kar.shell;
+
+import org.apache.karaf.cellar.core.CellarSupport;
+import org.apache.karaf.cellar.core.Group;
+import org.apache.karaf.cellar.core.control.SwitchStatus;
+import org.apache.karaf.cellar.core.event.EventProducer;
+import org.apache.karaf.cellar.core.event.EventType;
+import org.apache.karaf.cellar.core.shell.CellarCommandSupport;
+import org.apache.karaf.cellar.kar.ClusterKarEvent;
+import org.apache.karaf.cellar.kar.Constants;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Reference;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+@Command(scope = "cluster", name = "kar-uninstall", description = "Uninstall a KAR from a cluster group")
+@Service
+public class UninstallKarCommand extends CellarCommandSupport {
+
+    @Argument(index = 0, name = "group", description = "The cluster group name", required = true, multiValued = false)
+    String groupName;
+
+    @Argument(index = 1, name = "name", description = "The name of the KAR file to uninstall from the cluster", required = true, multiValued = false)
+    String name;
+
+    @Reference
+    private EventProducer eventProducer;
+
+    @Override
+    protected Object doExecute() throws Exception {
+        // check if the group exists
+        Group group = groupManager.findGroupByName(groupName);
+        if (group == null) {
+            System.err.println("Cluster group " + groupName + " doesn't exist");
+            return null;
+        }
+
+        // check if the producer is ON
+        if (eventProducer.getSwitch().getStatus().equals(SwitchStatus.OFF)) {
+            System.err.println("Cluster event producer is OFF");
+            return null;
+        }
+
+        CellarSupport support = new CellarSupport();
+        support.setConfigurationAdmin(configurationAdmin);
+        support.setGroupManager(groupManager);
+        support.setClusterManager(clusterManager);
+
+        // check if the kar is allowed
+        if (support.isAllowed(group, Constants.CATEGORY, name, EventType.OUTBOUND)) {
+            // broadcast cluster event
+            ClusterKarEvent clusterEvent = new ClusterKarEvent(name, true);
+            clusterEvent.setSourceGroup(group);
+            clusterEvent.setInstall(false);
+            eventProducer.produce(clusterEvent);
+        } else {
+            System.err.println("KAR " + name + " is blocked outbound for cluster group " + groupName);
+        }
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/5cf48dd7/manual/src/main/asciidoc/user-guide/groups.adoc
----------------------------------------------------------------------
diff --git a/manual/src/main/asciidoc/user-guide/groups.adoc b/manual/src/main/asciidoc/user-guide/groups.adoc
index 74ae35f..afcb3a1 100644
--- a/manual/src/main/asciidoc/user-guide/groups.adoc
+++ b/manual/src/main/asciidoc/user-guide/groups.adoc
@@ -286,6 +286,26 @@ You can modify this list using the same command, or by editing the `etc/org.apac
 config.excluded.properties = service.factoryPid, felix.fileinstall.filename, felix.fileinstall.dir, felix.fileinstall.tmpdir, org.ops4j.pax.url.mvn.defaultRepositories
 ----
 
+===== KAR
+
+Karaf Cellar is able to send cluster event for KAR files installation and uninstallation.
+
+However, due to KAR limitations, it's not possible to fully store the KAR files state on the cluster.
+
+To install a KAR file on the cluster, you have to use the `cluster:kar-install` command:
+
+----
+karaf@root()> cluster:kar-install cluster_group mvn:...kar
+----
+
+This will send a cluster event to all members of the cluster group and it will install the KAR file on those nodes.
+
+On the other hand, you can uninstall a KAR from a cluster group using `cluster:kar-uninstall` command:
+
+----
+karaf@root()> cluster:kar-uninstall cluster_group kar_name
+----
+
 ===== OBR (optional)
 
 See the link:obr[OBR section] for details.

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/5cf48dd7/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 9da01ac..d400188 100644
--- a/pom.xml
+++ b/pom.xml
@@ -57,6 +57,7 @@
         <module>core</module>
         <module>config</module>
         <module>features</module>
+        <module>kar</module>
         <module>bundle</module>
         <module>obr</module>
         <module>dosgi</module>
@@ -234,6 +235,11 @@
                 <version>${karaf.version}</version>
             </dependency>
             <dependency>
+                <groupId>org.apache.karaf.kar</groupId>
+                <artifactId>org.apache.karaf.kar.core</artifactId>
+                <version>${karaf.version}</version>
+            </dependency>
+            <dependency>
                 <groupId>org.apache.karaf.shell</groupId>
                 <artifactId>org.apache.karaf.shell.core</artifactId>
                 <version>${karaf.version}</version>