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 2019/10/18 14:18:11 UTC
[karaf-cave] branch master updated: Complete refactoring of Cave
This is an automated email from the ASF dual-hosted git repository.
jbonofre pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/karaf-cave.git
The following commit(s) were added to refs/heads/master by this push:
new 63859a1 Complete refactoring of Cave
new bbe77f1 Merge pull request #27 from jbonofre/REFACTORING
63859a1 is described below
commit 63859a1954347ad4c427fffaadfdc0d2a9f3b8cb
Author: Jean-Baptiste Onofré <jb...@apache.org>
AuthorDate: Mon Sep 23 19:36:19 2019 +0200
Complete refactoring of Cave
---
assembly/pom.xml | 9 +-
assembly/src/main/resources/features.xml | 101 +-
assembly/src/main/resources/filesystem.cfg | 23 -
deployer/api/pom.xml | 4 +-
.../karaf/cave/deployer/{api => }/Bundle.java | 2 +-
.../karaf/cave/deployer/{api => }/Config.java | 2 +-
.../karaf/cave/deployer/{api => }/Connection.java | 2 +-
.../{api/Deployer.java => DeployerService.java} | 4 +-
.../karaf/cave/deployer/{api => }/Feature.java | 2 +-
.../deployer/{api => }/FeaturesRepository.java | 2 +-
deployer/command/pom.xml | 66 --
deployer/management/pom.xml | 77 --
.../deployer/management/internal/Activator.java | 53 -
deployer/pom.xml | 4 +-
deployer/service/pom.xml | 120 +-
.../{impl => }/ConsoleRepositoryListener.java | 2 +-
.../{impl => }/ConsoleTransferListener.java | 2 +-
.../DeployerImpl.java => DeployerServiceImpl.java} | 48 +-
.../service}/command/AssembleFeatureCommand.java | 6 +-
.../service}/command/BundleInstallCommand.java | 8 +-
.../service}/command/BundleListCommand.java | 10 +-
.../service}/command/BundleStartCommand.java | 8 +-
.../service}/command/BundleStopCommand.java | 8 +-
.../service}/command/BundleUninstallCommand.java | 8 +-
.../command/ClusterFeatureInstallCommand.java | 8 +-
.../ClusterFeatureRepositoryAddCommand.java | 8 +-
.../ClusterFeatureRepositoryRemoveCommand.java | 8 +-
.../command/ClusterFeatureUninstallCommand.java | 8 +-
.../service}/command/ClusterGroupListCommand.java | 8 +-
.../service}/command/ClusterNodeListCommand.java | 8 +-
.../service}/command/ConfigCreateCommand.java | 8 +-
.../service}/command/ConfigDeleteCommand.java | 8 +-
.../command/ConfigFactoryCreateCommand.java | 8 +-
.../service}/command/ConfigListCommand.java | 8 +-
.../command/ConfigPropertyAppendCommand.java | 8 +-
.../command/ConfigPropertyDeleteCommand.java | 8 +-
.../command/ConfigPropertyListCommand.java | 8 +-
.../service}/command/ConfigPropertySetCommand.java | 8 +-
.../service}/command/ConnectionDeleteCommand.java | 8 +-
.../service}/command/ConnectionListCommand.java | 12 +-
.../command/ConnectionRegisterCommand.java | 8 +-
.../deployer/service}/command/DownloadCommand.java | 6 +-
.../deployer/service}/command/ExplodeCommand.java | 6 +-
.../deployer/service}/command/ExtractCommand.java | 6 +-
.../service}/command/FeatureInstallCommand.java | 8 +-
.../command/FeatureInstalledListCommand.java | 8 +-
.../service}/command/FeatureListCommand.java | 10 +-
.../service}/command/FeatureUninstallCommand.java | 8 +-
.../command/FeaturesRepositoryAddCommand.java | 8 +-
.../command/FeaturesRepositoryListCommand.java | 10 +-
.../command/FeaturesRepositoryProvideCommand.java | 8 +-
.../command/FeaturesRepositoryRemoveCommand.java | 8 +-
.../service}/command/KarInstallCommand.java | 8 +-
.../deployer/service}/command/KarListCommand.java | 8 +-
.../service}/command/KarUninstallCommand.java | 8 +-
.../deployer/service}/command/UploadCommand.java | 6 +-
.../command/completers/ConnectionCompleter.java | 8 +-
.../cave/deployer/service/impl/Activator.java | 40 -
.../service/management/DeployerMBean.java} | 4 +-
.../service/management/DeployerMBeanService.java} | 30 +-
.../deployer/service/rest/DeployerRestApi.java | 253 ++++
.../deployer/service/rest/DeployerRestService.java | 69 ++
.../deployer/service/rest/DeployerRestServlet.java | 42 +-
.../service/{impl => }/DeployerImplTest.java | 17 +-
{server => gateway}/api/pom.xml | 20 +-
.../karaf/cave/gateway/FeaturesGatewayService.java | 25 +-
{server => gateway}/pom.xml | 14 +-
{server/maven => gateway/service}/pom.xml | 107 +-
.../service/FeaturesGatewayServiceImpl.java | 132 +++
.../command/FeaturesGatewayListCommand.java | 12 +-
.../command/FeaturesGatewayRegisterCommand.java | 22 +-
.../command/FeaturesGatewayRemoveCommand.java | 23 +-
.../completers/FeaturesRepositoryUrlCompleter.java | 12 +-
.../service/http/FeaturesGatewayServlet.java | 54 +
.../service/management/FeaturesGatewayMBean.java | 11 +-
.../management/FeaturesGatewayMBeanService.java | 48 +
.../service/rest/FeaturesGatewayRestApi.java | 60 +
.../service/rest/FeaturesGatewayRestService.java | 68 ++
.../service/rest/FeaturesGatewayRestServlet.java | 41 +-
.../service/FeaturesGatewayServiceImplTest.java | 55 +
itest/pom.xml | 133 +++
.../karaf/cave/itest/repository/AllInTest.java | 69 ++
.../karaf/cave/itest/repository/DeployerTest.java | 78 ++
.../cave/itest/repository/FeaturesGatewayTest.java | 66 ++
.../cave/itest/repository/RepositoryTest.java | 155 +++
.../test/resources/etc/org.ops4j.pax.logging.cfg | 69 ++
manual/pom.xml | 2 +-
manual/src/main/asciidoc/index.adoc | 20 +-
manual/src/main/asciidoc/overview.adoc | 10 +
.../asciidoc/user-guide/administrate-cave.adoc | 54 -
.../main/asciidoc/user-guide/cave-repository.adoc | 103 --
manual/src/main/asciidoc/user-guide/deployer.adoc | 432 ++++++-
.../main/asciidoc/user-guide/features-gateway.adoc | 125 +-
.../src/main/asciidoc/user-guide/http-wrapper.adoc | 49 -
.../src/main/asciidoc/user-guide/installation.adoc | 55 -
.../main/asciidoc/user-guide/maven-wrapper.adoc | 54 -
.../asciidoc/user-guide/populate-repository.adoc | 81 --
.../main/asciidoc/user-guide/proxy-repository.adoc | 56 -
.../src/main/asciidoc/user-guide/repository.adoc | 583 ++++++++++
pom.xml | 78 +-
{server/command => repository/api}/pom.xml | 33 +-
.../apache/karaf/cave/repository/Repository.java | 238 ++++
.../karaf/cave/repository/RepositoryService.java | 220 ++++
{deployer => repository}/pom.xml | 8 +-
{deployer => repository}/service/pom.xml | 232 ++--
.../repository/service/RepositoryServiceImpl.java | 989 ++++++++++++++++
.../service/bundlerepository/BaseClause.java | 109 ++
.../service/bundlerepository/BaseRepository.java | 105 ++
.../service/bundlerepository/BundleRepository.java | 20 +-
.../service/bundlerepository/CapabilityImpl.java | 83 ++
.../service/bundlerepository/CapabilitySet.java | 463 ++++++++
.../service/bundlerepository/RequirementImpl.java | 81 ++
.../service/bundlerepository/ResourceBuilder.java | 1203 ++++++++++++++++++++
.../service/bundlerepository/ResourceImpl.java | 111 ++
.../service/bundlerepository/ResourceUtils.java | 162 +++
.../service/bundlerepository/SimpleFilter.java | 573 ++++++++++
.../service/bundlerepository/StaxParser.java | 447 ++++++++
.../service/bundlerepository/UrlLoader.java | 105 ++
.../service/bundlerepository/XmlRepository.java | 183 +++
.../command/RepositoryAddArtifactCommand.java | 30 +-
.../service/command/RepositoryCopyCommand.java | 36 +-
.../service/command/RepositoryCreateCommand.java | 73 ++
.../command/RepositoryDeleteArtifactCommand.java | 31 +-
.../service/command/RepositoryInfoCommand.java | 61 +
.../service/command/RepositoryListCommand.java | 28 +-
.../service/command/RepositoryLocationCommand.java | 33 +-
.../service/command/RepositoryProxyCommand.java | 63 +
.../service/command/RepositoryPurgeCommand.java | 27 +-
.../service/command/RepositoryRemoveCommand.java | 31 +-
.../service/command/RepositoryScheduleCommand.java | 58 +
.../service/command/RepositorySecurityCommand.java | 64 ++
.../RepositoryUpdateBundleDescriptorCommand.java | 27 +-
.../service/command/RepositoryUrlCommand.java | 33 +-
.../completers/RepositoryNameCompleter.java | 22 +-
.../service/management/RepositoryMBean.java | 36 +
.../service/management/RepositoryMBeanService.java | 131 +++
.../service/maven}/ConsoleRepositoryListener.java | 4 +-
.../service/maven}/ConsoleTransferListener.java | 4 +-
.../repository/service}/maven/DefaultFuture.java | 4 +-
.../repository/service}/maven/FutureListener.java | 4 +-
.../maven/InvalidMavenArtifactRequest.java | 4 +-
.../repository/service/maven/MavenServlet.java | 13 +-
.../repository/service}/maven/ThreadFactory.java | 4 +-
.../repository/service/rest/RepositoryRestApi.java | 110 ++
.../service/rest/RepositoryRestService.java | 68 ++
.../service/rest/RepositoryRestServlet.java | 42 +-
.../service/scheduler/RepositoryJob.java | 72 ++
.../service/RepositoryServiceImplTest.java | 279 +++++
rest/pom.xml | 121 --
.../java/org/apache/karaf/cave/rest/Activator.java | 67 --
.../org/apache/karaf/cave/rest/DeployerRest.java | 527 ---------
.../org/apache/karaf/cave/rest/RepositoryRest.java | 175 ---
.../karaf/cave/deployer/rest/DeployerRestTest.java | 60 -
rest/src/test/resources/test.kar | Bin 2895 -> 0 bytes
server/api/NOTICE | 29 -
.../server/api/CaveMavenRepositoryListener.java | 28 -
.../karaf/cave/server/api/CaveRepository.java | 166 ---
.../cave/server/api/CaveRepositoryService.java | 95 --
server/command/NOTICE | 29 -
.../command/CaveRepositoryCommandSupport.java | 42 -
.../cave/server/command/GatewayRemoveCommand.java | 45 -
.../server/command/RepositoryCreateCommand.java | 73 --
.../server/command/RepositoryDestroyCommand.java | 51 -
.../server/command/RepositoryInstallCommand.java | 45 -
.../server/command/RepositoryPopulateCommand.java | 65 --
.../server/command/RepositoryProxyCommand.java | 66 --
.../server/command/RepositoryUninstallCommand.java | 45 -
.../server/command/RepositoryUpdateCommand.java | 47 -
.../command/RepositoryUploadArtifactCommand.java | 59 -
server/http/NOTICE | 29 -
server/http/pom.xml | 88 --
.../apache/karaf/cave/server/http/Activator.java | 82 --
.../karaf/cave/server/http/CaveHttpServlet.java | 257 -----
server/management/NOTICE | 29 -
server/management/pom.xml | 76 --
.../server/management/CaveRepositoryMBean.java | 34 -
.../cave/server/management/internal/Activator.java | 67 --
.../management/internal/CaveGatewayMBeanImpl.java | 49 -
.../internal/CaveRepositoryMBeanImpl.java | 167 ---
.../apache/karaf/cave/server/maven/Activator.java | 107 --
.../maven/CaveMavenRepositoryListenerImpl.java | 60 -
.../cave/server/maven/MavenProxyServletTest.java | 743 ------------
server/storage/NOTICE | 29 -
server/storage/pom.xml | 115 --
.../karaf/cave/server/storage/Activator.java | 51 -
.../server/storage/CaveFeatureGatewayImpl.java | 96 --
.../cave/server/storage/CaveRepositoryImpl.java | 699 ------------
.../server/storage/CaveRepositoryServiceImpl.java | 329 ------
.../apache/karaf/cave/server/storage/Utils.java | 160 ---
.../server/storage/CaveFeatureGatewayImplTest.java | 80 --
.../server/storage/CaveRepositoryImplTest.java | 95 --
server/storage/src/test/resources/log4j.xml | 35 -
192 files changed, 9231 insertions(+), 7104 deletions(-)
diff --git a/assembly/pom.xml b/assembly/pom.xml
index 7706070..9385e03 100644
--- a/assembly/pom.xml
+++ b/assembly/pom.xml
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.karaf</groupId>
<artifactId>cave</artifactId>
- <version>4.1.3-SNAPSHOT</version>
+ <version>4.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
@@ -85,7 +85,7 @@
<feature>framework</feature>
</framework>
<features>
- <feature>!cave-rest</feature>
+ <feature>!*</feature>
</features>
<verifyTransitive>false</verifyTransitive>
</configuration>
@@ -130,11 +130,6 @@
<type>xml</type>
<classifier>features</classifier>
</artifact>
- <artifact>
- <file>target/classes/filesystem.cfg</file>
- <type>cfg</type>
- <classifier>filesystem</classifier>
- </artifact>
</artifacts>
</configuration>
</execution>
diff --git a/assembly/src/main/resources/features.xml b/assembly/src/main/resources/features.xml
index 18983b6..bc847b2 100644
--- a/assembly/src/main/resources/features.xml
+++ b/assembly/src/main/resources/features.xml
@@ -16,95 +16,50 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<features name="karaf-cave-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.3.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.3.0 http://karaf.apache.org/xmlns/features/v1.3.0">
+<features name="karaf-cave-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.4.0 http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.apache.cxf.karaf/apache-cxf/${cxf.version}/xml/features</repository>
- <feature name="cave" version="${project.version}">
- <feature prerequisite="true">cave-server</feature>
- <feature prerequisite="true">cave-deployer</feature>
- <feature>cave-rest</feature>
- </feature>
-
- <feature name="cave-server" version="${project.version}">
- <feature>cave-server-storage</feature>
- <feature>cave-server-http</feature>
- <feature>cave-server-maven</feature>
- </feature>
-
- <feature name="cave-server-storage" version="${project.version}">
- <configfile finalname="/etc/org.apache.karaf.cave.server.storage.cfg">mvn:org.apache.karaf.cave/apache-karaf-cave/${project.version}/cfg/filesystem</configfile>
- <bundle>mvn:org.jsoup/jsoup/${jsoup.version}</bundle>
- <bundle>mvn:commons-codec/commons-codec/${commons-codec.version}</bundle>
- <bundle>mvn:org.apache.karaf.cave.server/org.apache.karaf.cave.server.api/${project.version}</bundle>
- <bundle>mvn:org.apache.karaf.cave.server/org.apache.karaf.cave.server.storage/${project.version}</bundle>
- <conditional>
- <condition>management</condition>
- <bundle>mvn:org.apache.karaf.cave.server/org.apache.karaf.cave.server.management/${project.version}</bundle>
- </conditional>
- <conditional>
- <condition>shell</condition>
- <bundle>mvn:org.apache.karaf.cave.server/org.apache.karaf.cave.server.command/${project.version}</bundle>
- </conditional>
- </feature>
-
- <feature name="cave-server-http" version="${project.version}">
+ <feature name="cave-common" version="${project.version}">
<feature prerequisite="true">http</feature>
- <requirement>osgi.service;effective:=active;filter:=(objectClass=org.osgi.service.http.HttpService)</requirement>
- <feature>cave-server-storage</feature>
- <bundle>mvn:org.apache.karaf.cave.server/org.apache.karaf.cave.server.http/${project.version}</bundle>
+ <feature prerequisite="true">scr</feature>
+ <feature version="${cxf.version}">cxf-jaxrs</feature>
+ <bundle dependency="true">mvn:com.fasterxml.jackson.core/jackson-core/${jackson.version}</bundle>
+ <bundle dependency="true">mvn:com.fasterxml.jackson.core/jackson-annotations/${jackson.version}</bundle>
+ <bundle dependency="true">mvn:com.fasterxml.jackson.core/jackson-databind/${jackson.version}</bundle>
+ <bundle dependency="true">mvn:com.fasterxml.jackson.jaxrs/jackson-jaxrs-base/${jackson.version}</bundle>
+ <bundle dependency="true">mvn:com.fasterxml.jackson.jaxrs/jackson-jaxrs-json-provider/${jackson.version}</bundle>
</feature>
- <feature name="cave-server-maven" version="${project.version}">
- <feature prerequisite="true">http</feature>
- <requirement>osgi.service;effective:=active;filter:=(objectClass=org.osgi.service.http.HttpService)</requirement>
- <bundle>mvn:org.apache.karaf.cave.server/org.apache.karaf.cave.server.maven/${project.version}</bundle>
+ <feature name="cave-repository-api" version="${project.version}">
+ <bundle>mvn:org.apache.karaf.cave.repository/org.apache.karaf.cave.repository.api/${project.version}</bundle>
</feature>
- <feature name="cave-deployer" version="${project.version}">
- <feature>cave-deployer-service</feature>
- <feature>cave-deployer-command</feature>
- <feature>cave-deployer-management</feature>
+ <feature name="cave-repository" version="${project.version}">
+ <feature prerequisite="true">cave-common</feature>
+ <feature version="[4.2,4.3)">cave-repository-api</feature>
+ <feature prerequisite="true">scheduler</feature>
+ <bundle>mvn:org.apache.karaf.cave.repository/org.apache.karaf.cave.repository.service/${project.version}</bundle>
</feature>
- <feature name="cave-deployer-service" version="${project.version}">
- <bundle>mvn:org.apache.karaf.cave.deployer/org.apache.karaf.cave.deployer.api/${project.version}</bundle>
- <bundle dependency="true">mvn:org.apache.httpcomponents/httpcore-osgi/4.2.5</bundle>
- <bundle dependency="true">mvn:org.apache.httpcomponents/httpclient-osgi/4.2.5</bundle>
- <bundle dependency="true">mvn:com.google.inject/guice/3.0</bundle>
- <bundle dependency="true">mvn:com.google.guava/guava/18.0</bundle>
- <bundle>mvn:org.apache.karaf.cave.deployer/org.apache.karaf.cave.deployer.service/${project.version}</bundle>
- <config name="org.apache.karaf.cave.deployer">
- # Karaf Cave Deployer connection storage
- </config>
+ <feature name="cave-features-gateway-api" version="${project.version}">
+ <bundle>mvn:org.apache.karaf.cave.gateway/org.apache.karaf.cave.gateway.api/${project.version}</bundle>
</feature>
- <feature name="cave-deployer-command" version="${project.version}">
- <feature version="${project.version}">cave-deployer-service</feature>
- <conditional>
- <condition>shell</condition>
- <bundle>mvn:org.apache.karaf.cave.deployer/org.apache.karaf.cave.deployer.command/${project.version}</bundle>
- </conditional>
+ <feature name="cave-features-gateway" version="${project.version}">
+ <feature prerequisite="true">cave-common</feature>
+ <feature version="[4.2,4.3)">cave-features-gateway-api</feature>
+ <bundle>mvn:org.apache.karaf.cave.gateway/org.apache.karaf.cave.gateway.service/${project.version}</bundle>
</feature>
- <feature name="cave-deployer-management" version="${project.version}">
- <feature version="${project.version}">cave-deployer-service</feature>
- <conditional>
- <condition>management</condition>
- <bundle>mvn:org.apache.karaf.cave.deployer/org.apache.karaf.cave.deployer.management/${project.version}</bundle>
- </conditional>
+ <feature name="cave-deployer-api" version="${project.version}">
+ <bundle>mvn:org.apache.karaf.cave.deployer/org.apache.karaf.cave.deployer.api/${project.version}</bundle>
</feature>
- <feature name="cave-rest" version="${project.version}">
- <feature prerequisite="true">http</feature>
- <requirement>osgi.service;effective:=active;filter:=(objectClass=org.osgi.service.http.HttpService)</requirement>
- <feature version="${cxf.version}">cxf-jaxrs</feature>
- <bundle dependency="true">mvn:com.fasterxml.jackson.core/jackson-core/2.4.6</bundle>
- <bundle dependency="true">mvn:com.fasterxml.jackson.core/jackson-annotations/2.4.6</bundle>
- <bundle dependency="true">mvn:com.fasterxml.jackson.core/jackson-databind/2.4.6</bundle>
- <bundle dependency="true">mvn:com.fasterxml.jackson.jaxrs/jackson-jaxrs-base/2.4.6</bundle>
- <bundle dependency="true">mvn:com.fasterxml.jackson.jaxrs/jackson-jaxrs-json-provider/2.4.6</bundle>
- <bundle>mvn:org.apache.karaf.cave/org.apache.karaf.cave.rest/${project.version}</bundle>
+ <feature name="cave-deployer" version="${project.version}">
+ <feature prerequisite="true">cave-common</feature>
+ <feature version="[4.2,4.3)">cave-deployer-api</feature>
+ <bundle>mvn:org.apache.karaf.cave.deployer/org.apache.karaf.cave.deployer.service/${project.version}</bundle>
</feature>
<feature name="cave-documentation" description="Documentation of Karaf Cave sub-project in HTML" version="${project.version}">
diff --git a/assembly/src/main/resources/filesystem.cfg b/assembly/src/main/resources/filesystem.cfg
deleted file mode 100644
index e314f4b..0000000
--- a/assembly/src/main/resources/filesystem.cfg
+++ /dev/null
@@ -1,23 +0,0 @@
-################################################################################
-#
-# 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.
-#
-################################################################################
-
-#
-# Storage location where Apache Karaf Cave create repositories by default
-#
-cave.storage.location=${karaf.data}/cave
\ No newline at end of file
diff --git a/deployer/api/pom.xml b/deployer/api/pom.xml
index 5d8ceee..7aeb258 100644
--- a/deployer/api/pom.xml
+++ b/deployer/api/pom.xml
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.karaf.cave</groupId>
<artifactId>org.apache.karaf.cave.deployer</artifactId>
- <version>4.1.3-SNAPSHOT</version>
+ <version>4.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
@@ -43,7 +43,7 @@
<configuration>
<instructions>
<Export-Package>
- org.apache.karaf.cave.deployer.api
+ org.apache.karaf.cave.deployer
</Export-Package>
</instructions>
</configuration>
diff --git a/deployer/api/src/main/java/org/apache/karaf/cave/deployer/api/Bundle.java b/deployer/api/src/main/java/org/apache/karaf/cave/deployer/Bundle.java
similarity index 97%
rename from deployer/api/src/main/java/org/apache/karaf/cave/deployer/api/Bundle.java
rename to deployer/api/src/main/java/org/apache/karaf/cave/deployer/Bundle.java
index fbdb4c2..ce2b3a8 100644
--- a/deployer/api/src/main/java/org/apache/karaf/cave/deployer/api/Bundle.java
+++ b/deployer/api/src/main/java/org/apache/karaf/cave/deployer/Bundle.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.api;
+package org.apache.karaf.cave.deployer;
/**
* Simplified wrapper representing a bundle.
diff --git a/deployer/api/src/main/java/org/apache/karaf/cave/deployer/api/Config.java b/deployer/api/src/main/java/org/apache/karaf/cave/deployer/Config.java
similarity index 96%
rename from deployer/api/src/main/java/org/apache/karaf/cave/deployer/api/Config.java
rename to deployer/api/src/main/java/org/apache/karaf/cave/deployer/Config.java
index ea7cff8..0405eb5 100644
--- a/deployer/api/src/main/java/org/apache/karaf/cave/deployer/api/Config.java
+++ b/deployer/api/src/main/java/org/apache/karaf/cave/deployer/Config.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.api;
+package org.apache.karaf.cave.deployer;
import java.util.HashMap;
import java.util.Map;
diff --git a/deployer/api/src/main/java/org/apache/karaf/cave/deployer/api/Connection.java b/deployer/api/src/main/java/org/apache/karaf/cave/deployer/Connection.java
similarity index 97%
rename from deployer/api/src/main/java/org/apache/karaf/cave/deployer/api/Connection.java
rename to deployer/api/src/main/java/org/apache/karaf/cave/deployer/Connection.java
index 03797be..bbd573b 100644
--- a/deployer/api/src/main/java/org/apache/karaf/cave/deployer/api/Connection.java
+++ b/deployer/api/src/main/java/org/apache/karaf/cave/deployer/Connection.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.api;
+package org.apache.karaf.cave.deployer;
/**
* Simple wrapper describing a connection to a Karaf instance.
diff --git a/deployer/api/src/main/java/org/apache/karaf/cave/deployer/api/Deployer.java b/deployer/api/src/main/java/org/apache/karaf/cave/deployer/DeployerService.java
similarity index 99%
rename from deployer/api/src/main/java/org/apache/karaf/cave/deployer/api/Deployer.java
rename to deployer/api/src/main/java/org/apache/karaf/cave/deployer/DeployerService.java
index a2f5014..a2ffdbb 100644
--- a/deployer/api/src/main/java/org/apache/karaf/cave/deployer/api/Deployer.java
+++ b/deployer/api/src/main/java/org/apache/karaf/cave/deployer/DeployerService.java
@@ -14,12 +14,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.api;
+package org.apache.karaf.cave.deployer;
import java.util.List;
import java.util.Map;
-public interface Deployer {
+public interface DeployerService {
/**
* Register a connection in the Deployer service.
diff --git a/deployer/api/src/main/java/org/apache/karaf/cave/deployer/api/Feature.java b/deployer/api/src/main/java/org/apache/karaf/cave/deployer/Feature.java
similarity index 96%
rename from deployer/api/src/main/java/org/apache/karaf/cave/deployer/api/Feature.java
rename to deployer/api/src/main/java/org/apache/karaf/cave/deployer/Feature.java
index da1c034..8f3c0e7 100644
--- a/deployer/api/src/main/java/org/apache/karaf/cave/deployer/api/Feature.java
+++ b/deployer/api/src/main/java/org/apache/karaf/cave/deployer/Feature.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.api;
+package org.apache.karaf.cave.deployer;
/**
* Simplified wrapper representing a feature.
diff --git a/deployer/api/src/main/java/org/apache/karaf/cave/deployer/api/FeaturesRepository.java b/deployer/api/src/main/java/org/apache/karaf/cave/deployer/FeaturesRepository.java
similarity index 96%
rename from deployer/api/src/main/java/org/apache/karaf/cave/deployer/api/FeaturesRepository.java
rename to deployer/api/src/main/java/org/apache/karaf/cave/deployer/FeaturesRepository.java
index 1bf704f..fde8a7d 100644
--- a/deployer/api/src/main/java/org/apache/karaf/cave/deployer/api/FeaturesRepository.java
+++ b/deployer/api/src/main/java/org/apache/karaf/cave/deployer/FeaturesRepository.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.api;
+package org.apache.karaf.cave.deployer;
/**
* Simplified wrapper representing a features repository.
diff --git a/deployer/command/pom.xml b/deployer/command/pom.xml
deleted file mode 100644
index 20fad4d..0000000
--- a/deployer/command/pom.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-<?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.cave</groupId>
- <artifactId>org.apache.karaf.cave.deployer</artifactId>
- <version>4.1.3-SNAPSHOT</version>
- <relativePath>../pom.xml</relativePath>
- </parent>
-
- <groupId>org.apache.karaf.cave.deployer</groupId>
- <artifactId>org.apache.karaf.cave.deployer.command</artifactId>
- <name>Apache Karaf :: Cave :: Deployer :: Command</name>
- <packaging>bundle</packaging>
-
- <dependencies>
- <dependency>
- <groupId>org.apache.karaf.shell</groupId>
- <artifactId>org.apache.karaf.shell.core</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.karaf.cave.deployer</groupId>
- <artifactId>org.apache.karaf.cave.deployer.api</artifactId>
- <version>${project.version}</version>
- </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>
- <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
- </instructions>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
-</project>
\ No newline at end of file
diff --git a/deployer/management/pom.xml b/deployer/management/pom.xml
deleted file mode 100644
index 0546786..0000000
--- a/deployer/management/pom.xml
+++ /dev/null
@@ -1,77 +0,0 @@
-<?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.cave</groupId>
- <artifactId>org.apache.karaf.cave.deployer</artifactId>
- <version>4.1.3-SNAPSHOT</version>
- <relativePath>../pom.xml</relativePath>
- </parent>
-
- <groupId>org.apache.karaf.cave.deployer</groupId>
- <artifactId>org.apache.karaf.cave.deployer.management</artifactId>
- <packaging>bundle</packaging>
- <name>Apache Karaf :: Cave :: Deployer :: Management</name>
-
- <dependencies>
- <dependency>
- <groupId>org.apache.karaf.cave.deployer</groupId>
- <artifactId>org.apache.karaf.cave.deployer.api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.apache.karaf</groupId>
- <artifactId>org.apache.karaf.util</artifactId>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>osgi.core</artifactId>
- </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>
- <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
- <Export-Package>
- org.apache.karaf.cave.deployer.management
- </Export-Package>
- <Private-Package>
- org.apache.karaf.cave.server.deployer.internal,
- org.apache.karaf.util.tracker*
- </Private-Package>
- </instructions>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
-</project>
\ No newline at end of file
diff --git a/deployer/management/src/main/java/org/apache/karaf/cave/deployer/management/internal/Activator.java b/deployer/management/src/main/java/org/apache/karaf/cave/deployer/management/internal/Activator.java
deleted file mode 100644
index 4daf8a0..0000000
--- a/deployer/management/src/main/java/org/apache/karaf/cave/deployer/management/internal/Activator.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.cave.deployer.management.internal;
-
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.util.tracker.BaseActivator;
-import org.apache.karaf.util.tracker.annotation.RequireService;
-import org.apache.karaf.util.tracker.annotation.Services;
-import org.osgi.framework.ServiceRegistration;
-
-import java.util.Hashtable;
-
-@Services(
- requires = {
- @RequireService(Deployer.class)
- }
-)
-public class Activator extends BaseActivator {
-
- private volatile ServiceRegistration mbeanRegistration;
-
- @Override
- protected void doStart() throws Exception {
- Deployer deployer = getTrackedService(Deployer.class);
- CaveDeployerMBeanImpl mbean = new CaveDeployerMBeanImpl();
- mbean.setDeployer(deployer);
-
- Hashtable<String, Object> props = new Hashtable<>();
- props.put("jmx.objectname", "org.apache.karaf.cave:type=deployer,name=" + System.getProperty("karaf.name"));
- mbeanRegistration = this.bundleContext.registerService(getInterfaceNames(mbean), mbean, props);
- }
-
- @Override
- protected void doStop() {
- if (mbeanRegistration != null) {
- mbeanRegistration.unregister();
- mbeanRegistration = null;
- }
- super.doStop();
- }
-
-}
diff --git a/deployer/pom.xml b/deployer/pom.xml
index 5d23b7e..7e002b1 100644
--- a/deployer/pom.xml
+++ b/deployer/pom.xml
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.karaf</groupId>
<artifactId>cave</artifactId>
- <version>4.1.3-SNAPSHOT</version>
+ <version>4.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
@@ -36,8 +36,6 @@
<modules>
<module>api</module>
<module>service</module>
- <module>command</module>
- <module>management</module>
</modules>
</project>
\ No newline at end of file
diff --git a/deployer/service/pom.xml b/deployer/service/pom.xml
index 6009585..4c8bbb2 100644
--- a/deployer/service/pom.xml
+++ b/deployer/service/pom.xml
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.karaf.cave</groupId>
<artifactId>org.apache.karaf.cave.deployer</artifactId>
- <version>4.1.3-SNAPSHOT</version>
+ <version>4.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
@@ -252,6 +252,10 @@
<artifactId>org.apache.karaf.features.core</artifactId>
<version>${karaf.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.core</artifactId>
+ </dependency>
<!-- Logging -->
<dependency>
@@ -278,7 +282,18 @@
<version>1.7.25</version>
<scope>test</scope>
</dependency>
-
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-frontend-jaxrs</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${servlet.spec.groupId}</groupId>
+ <artifactId>${servlet.spec.artifactId}</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.jaxrs</groupId>
+ <artifactId>jackson-jaxrs-json-provider</artifactId>
+ </dependency>
</dependencies>
<build>
@@ -290,82 +305,51 @@
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
- <version>2.5.4</version>
<extensions>true</extensions>
<inherited>true</inherited>
<configuration>
<instructions>
<Import-Package>
- org.apache.karaf.cave.deployer.api,
- org.slf4j;resolution:=optional,
- org.junit;resolution:=optional,
- org.testng*;resolution:=optional,
- junit.framework.*;resolution:=optional,
- org.apache.maven.model.*;resolution:=optional,
- org.apache.maven.artifact.*;resolution:=optional,
- org.apache.maven.cli.*;resolution:=optional,
- org.apache.maven.settings.merge.*;resolution:=optional,
- org.apache.maven.wagon.events.*;resolution:=optional,
- org.apache.commons.cli.*;resolution:=optional,
- org.apache.tools.ant.*;resolution:=optional,
- org.codehaus.plexus.component.repository.exception.*,
- org.codehaus.plexus.component.annotations.*;resolution:=optional,
- org.codehaus.plexus.util.*;resolution:=optional,
- org.sonatype.plexus.components.cipher.*;resolution:=optional,
- org.sonatype.plexus.components.sec.dispatcher.*;resolution:=optional,
- hudson.maven.*;resolution:=optional,
- org.eclipse.aether.util.repository.layout.*;resolution:=optional,
- org.apache.maven.wagon.*;resolution:=optional,
+ !org.apache.http*,
+ !org.eclipse.aether*,
+ !org.sonatype*,
+ !net.spy.memcached,
+ !org.apache.commons.codec*,
ch.qos.logback*;resolution:=optional,
+ junit.framework;resolution:=optional,
+ org.junit;resolution:=optional,
+ org.slf4j.impl;resolution:=optional,
+ org.testng.annotations;resolution:=optional,
+ javax.xml.bind*;version="[2,3)",
+ net.sf.ehcache;resolution:=optional,
+ com.fasterxml.jackson*;version="[2.8,3)",
*
</Import-Package>
<Private-Package>
- org.apache.karaf.cave.deployer.service.impl,
- org.apache.karaf.features.internal.model,
- org.apache.felix.utils.version,
- org.apache.felix.utils.properties,
- org.apache.karaf.util
+ org.apache.karaf.cave.repository.service*,
+ org.apache.karaf.util*,
+ org.apache.maven*,
+ org.eclipse.aether*,
+ com.google*,
+ javax.inject,
+ org.apache.commons.cli,
+ org.apache.http*,
+ org.codehaus.plexus*,
+ org.codehaus.classworlds,
+ org.eclipse.sisu*,
+ javax.enterprise.inject,
+ javax.enterprise.context,
+ javax.enterprise.util,
+ org.sonatype*,
+ net.spy.memcached,
+ org.apache.commons.codec*,
+ org.apache.felix.utils.*,
+ org.apache.felix.resolver,
+ org.apache.felix.resolver.util,
+ org.apache.felix.resolver.reason,
+ org.apache.karaf.features.internal.model
</Private-Package>
- <Embed-Dependency>
- sisu-guice,
- sisu-guava,
- sisu-inject-bean,
- javax.interceptor-api,
- javax.el-api,
- cdi-api,
- aether-transport-http,
- aether-transport-file,
- aether-connector-basic,
- wagon-provider-api,
- aether-util,
- aether-spi,
- aether-impl,
- aether-api,
- aether-connector-wagon,
- maven-aether-provider,
- maven-model,
- maven-compat,
- ant,
- maven-settings-builder,
- maven-settings,
- org.eclipse.sisu.plexus,
- org.eclipse.sisu.inject,
- plexus-component-annotations,
- plexus-sec-dispatcher,
- plexus-cipher,
- commons-cli,
- maven-embedder,
- plexus-classworlds,
- maven-model-builder,
- maven-artifact,
- maven-repository-metadata,
- maven-core,
- maven-plugin-api,
- plexus-utils,
- plexus-interpolation
- </Embed-Dependency>
- <Embed-Directory>jars</Embed-Directory>
- <Embed-Transitive>true</Embed-Transitive>
+ <_dsannotations>*</_dsannotations>
</instructions>
</configuration>
</plugin>
diff --git a/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/impl/ConsoleRepositoryListener.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/ConsoleRepositoryListener.java
similarity index 98%
copy from deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/impl/ConsoleRepositoryListener.java
copy to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/ConsoleRepositoryListener.java
index 7db240c..873fe44 100644
--- a/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/impl/ConsoleRepositoryListener.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/ConsoleRepositoryListener.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.service.impl;
+package org.apache.karaf.cave.deployer.service;
import org.eclipse.aether.AbstractRepositoryListener;
import org.eclipse.aether.RepositoryEvent;
diff --git a/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/impl/ConsoleTransferListener.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/ConsoleTransferListener.java
similarity index 98%
copy from deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/impl/ConsoleTransferListener.java
copy to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/ConsoleTransferListener.java
index 3b8c37d..d1d092f 100644
--- a/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/impl/ConsoleTransferListener.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/ConsoleTransferListener.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.service.impl;
+package org.apache.karaf.cave.deployer.service;
import org.eclipse.aether.transfer.AbstractTransferListener;
import org.eclipse.aether.transfer.TransferEvent;
diff --git a/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/impl/DeployerImpl.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/DeployerServiceImpl.java
similarity index 97%
rename from deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/impl/DeployerImpl.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/DeployerServiceImpl.java
index 46d9fc0..7dbd846 100644
--- a/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/impl/DeployerImpl.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/DeployerServiceImpl.java
@@ -14,12 +14,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.service.impl;
+package org.apache.karaf.cave.deployer.service;
import com.google.common.io.Files;
-import org.apache.karaf.cave.deployer.api.Connection;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.api.FeaturesRepository;
+import org.apache.karaf.cave.deployer.Connection;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.FeaturesRepository;
import org.apache.karaf.features.internal.model.*;
import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
import org.eclipse.aether.DefaultRepositorySystemSession;
@@ -39,6 +39,8 @@ import org.eclipse.aether.transport.file.FileTransporterFactory;
import org.eclipse.aether.transport.http.HttpTransporterFactory;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
@@ -65,20 +67,18 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
-public class DeployerImpl implements Deployer {
+@Component(name = "org.apache.karaf.cave.deployer", immediate = true, service = DeployerService.class)
+public class DeployerServiceImpl implements DeployerService {
- private final static Logger LOGGER = LoggerFactory.getLogger(DeployerImpl.class);
+ @Reference
+ private ConfigurationAdmin configurationAdmin;
+
+ private final static Logger LOGGER = LoggerFactory.getLogger(DeployerServiceImpl.class);
private final static Pattern mvnPattern = Pattern.compile("mvn:([^/ ]+)/([^/ ]+)/([^/ ]*)(/([^/ ]+)(/([^/ ]+))?)?");
private final static String CONFIG_PID = "org.apache.karaf.cave.deployer";
- private ConfigurationAdmin configurationAdmin;
-
- public void setConfigurationAdmin(ConfigurationAdmin configurationAdmin) {
- this.configurationAdmin = configurationAdmin;
- }
-
@Override
public void registerConnection(Connection connection) throws Exception {
Configuration configuration = configurationAdmin.getConfiguration(CONFIG_PID);
@@ -425,7 +425,7 @@ public class DeployerImpl implements Deployer {
List<String> featuresRepositoryUrls,
List<String> features,
List<String> bundles,
- List<org.apache.karaf.cave.deployer.api.Config> configs) throws Exception {
+ List<org.apache.karaf.cave.deployer.Config> configs) throws Exception {
Features featuresModel = new Features();
featuresModel.setName(feature);
// add features repository
@@ -456,7 +456,7 @@ public class DeployerImpl implements Deployer {
}
// add config
if (configs != null) {
- for (org.apache.karaf.cave.deployer.api.Config config : configs) {
+ for (org.apache.karaf.cave.deployer.Config config : configs) {
Config modelConfig = new Config();
modelConfig.setName(config.getPid());
StringBuilder builder = new StringBuilder();
@@ -603,7 +603,7 @@ public class DeployerImpl implements Deployer {
}
@Override
- public List<org.apache.karaf.cave.deployer.api.Bundle> bundles(String connectionName) throws Exception {
+ public List<org.apache.karaf.cave.deployer.Bundle> bundles(String connectionName) throws Exception {
Connection connection = getConnection(connectionName);
JMXConnector jmxConnector = connect(connection.getJmxUrl(),
connection.getKarafName(),
@@ -613,7 +613,7 @@ public class DeployerImpl implements Deployer {
MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();
ObjectName name = new ObjectName("org.apache.karaf:type=bundle,name=" + connection.getKarafName());
TabularData tabularData = (TabularData) mBeanServerConnection.getAttribute(name, "Bundles");
- List<org.apache.karaf.cave.deployer.api.Bundle> result = new ArrayList<org.apache.karaf.cave.deployer.api.Bundle>();
+ List<org.apache.karaf.cave.deployer.Bundle> result = new ArrayList<org.apache.karaf.cave.deployer.Bundle>();
for (Object value : tabularData.values()) {
CompositeData compositeData = (CompositeData) value;
Long bundleId = (Long) compositeData.get("ID");
@@ -621,7 +621,7 @@ public class DeployerImpl implements Deployer {
String bundleVersion = (String) compositeData.get("Version");
String bundleState = (String) compositeData.get("State");
Integer bundleStartLevel = (Integer) compositeData.get("Start Level");
- org.apache.karaf.cave.deployer.api.Bundle bundle = new org.apache.karaf.cave.deployer.api.Bundle();
+ org.apache.karaf.cave.deployer.Bundle bundle = new org.apache.karaf.cave.deployer.Bundle();
bundle.setId(bundleId.toString());
bundle.setName(bundleName);
bundle.setVersion(bundleVersion);
@@ -637,11 +637,11 @@ public class DeployerImpl implements Deployer {
}
}
- public List<org.apache.karaf.cave.deployer.api.Feature> providedFeatures(String featureRepositoryUrl) throws Exception {
+ public List<org.apache.karaf.cave.deployer.Feature> providedFeatures(String featureRepositoryUrl) throws Exception {
Features features = JaxbUtil.unmarshal(featureRepositoryUrl, true);
- List<org.apache.karaf.cave.deployer.api.Feature> wrappedFeatures = new ArrayList<>();
+ List<org.apache.karaf.cave.deployer.Feature> wrappedFeatures = new ArrayList<>();
for (Feature feature : features.getFeature()) {
- org.apache.karaf.cave.deployer.api.Feature wrappedFeature = new org.apache.karaf.cave.deployer.api.Feature();
+ org.apache.karaf.cave.deployer.Feature wrappedFeature = new org.apache.karaf.cave.deployer.Feature();
wrappedFeature.setName(feature.getName());
wrappedFeature.setVersion(feature.getVersion());
wrappedFeatures.add(wrappedFeature);
@@ -751,7 +751,7 @@ public class DeployerImpl implements Deployer {
}
@Override
- public List<org.apache.karaf.cave.deployer.api.Feature> features(String connectionName) throws Exception {
+ public List<org.apache.karaf.cave.deployer.Feature> features(String connectionName) throws Exception {
Connection connection = getConnection(connectionName);
JMXConnector jmxConnector = connect(connection.getJmxUrl(),
connection.getKarafName(),
@@ -761,13 +761,13 @@ public class DeployerImpl implements Deployer {
MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();
ObjectName name = new ObjectName("org.apache.karaf:type=feature,name=" + connection.getKarafName());
TabularData tabularData = (TabularData) mBeanServerConnection.getAttribute(name, "Features");
- List<org.apache.karaf.cave.deployer.api.Feature> result = new ArrayList<>();
+ List<org.apache.karaf.cave.deployer.Feature> result = new ArrayList<>();
for (Object value : tabularData.values()) {
CompositeData compositeData = (CompositeData) value;
String featureName = (String) compositeData.get("Name");
String featureVersion = (String) compositeData.get("Version");
boolean featureInstalled = (Boolean) compositeData.get("Installed");
- org.apache.karaf.cave.deployer.api.Feature feature = new org.apache.karaf.cave.deployer.api.Feature();
+ org.apache.karaf.cave.deployer.Feature feature = new org.apache.karaf.cave.deployer.Feature();
feature.setName(featureName);
feature.setVersion(featureVersion);
if (featureInstalled)
@@ -1003,7 +1003,7 @@ public class DeployerImpl implements Deployer {
}
@Override
- public void updateConfig(org.apache.karaf.cave.deployer.api.Config config, String connectionName) throws Exception {
+ public void updateConfig(org.apache.karaf.cave.deployer.Config config, String connectionName) throws Exception {
Connection connection = getConnection(connectionName);
JMXConnector jmxConnector = connect(connection.getJmxUrl(),
connection.getKarafName(),
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/AssembleFeatureCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/AssembleFeatureCommand.java
similarity index 95%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/AssembleFeatureCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/AssembleFeatureCommand.java
index 0badc22..b6618ae 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/AssembleFeatureCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/AssembleFeatureCommand.java
@@ -14,9 +14,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
+import org.apache.karaf.cave.deployer.DeployerService;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -31,7 +31,7 @@ import java.util.List;
public class AssembleFeatureCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Option(name = "-g", aliases = "--groupId", description = "Maven groupId", required = true, multiValued = false)
String groupId;
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/BundleInstallCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/BundleInstallCommand.java
similarity index 88%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/BundleInstallCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/BundleInstallCommand.java
index 3e3616c..2e6a6a0 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/BundleInstallCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/BundleInstallCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -30,7 +30,7 @@ import org.apache.karaf.shell.api.action.lifecycle.Service;
public class BundleInstallCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "The connection to use", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/BundleListCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/BundleListCommand.java
similarity index 88%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/BundleListCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/BundleListCommand.java
index 5d9e38b..da64c75 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/BundleListCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/BundleListCommand.java
@@ -14,11 +14,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Bundle;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.Bundle;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -34,7 +34,7 @@ import java.util.List;
public class BundleListCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "connection", description = "The connection to use", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/BundleStartCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/BundleStartCommand.java
similarity index 88%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/BundleStartCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/BundleStartCommand.java
index 2ea9e7f..53e085f 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/BundleStartCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/BundleStartCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -30,7 +30,7 @@ import org.apache.karaf.shell.api.action.lifecycle.Service;
public class BundleStartCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "connection", description = "The connection to use", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/BundleStopCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/BundleStopCommand.java
similarity index 88%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/BundleStopCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/BundleStopCommand.java
index 1368266..631fcfe 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/BundleStopCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/BundleStopCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -30,7 +30,7 @@ import org.apache.karaf.shell.api.action.lifecycle.Service;
public class BundleStopCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "connection", description = "The connection to use", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/BundleUninstallCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/BundleUninstallCommand.java
similarity index 88%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/BundleUninstallCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/BundleUninstallCommand.java
index 4ee4a1b..9a2d814 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/BundleUninstallCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/BundleUninstallCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -30,7 +30,7 @@ import org.apache.karaf.shell.api.action.lifecycle.Service;
public class BundleUninstallCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "connection", description = "The connection to use", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ClusterFeatureInstallCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ClusterFeatureInstallCommand.java
similarity index 89%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ClusterFeatureInstallCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ClusterFeatureInstallCommand.java
index 4fc92df..c7462a3 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ClusterFeatureInstallCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ClusterFeatureInstallCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -30,7 +30,7 @@ import org.apache.karaf.shell.api.action.lifecycle.Service;
public class ClusterFeatureInstallCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "connection", description = "The connection to use", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ClusterFeatureRepositoryAddCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ClusterFeatureRepositoryAddCommand.java
similarity index 89%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ClusterFeatureRepositoryAddCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ClusterFeatureRepositoryAddCommand.java
index 7feaff6..4cc5bd9 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ClusterFeatureRepositoryAddCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ClusterFeatureRepositoryAddCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -30,7 +30,7 @@ import org.apache.karaf.shell.api.action.lifecycle.Service;
public class ClusterFeatureRepositoryAddCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "connection", description = "The connection to use", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ClusterFeatureRepositoryRemoveCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ClusterFeatureRepositoryRemoveCommand.java
similarity index 89%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ClusterFeatureRepositoryRemoveCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ClusterFeatureRepositoryRemoveCommand.java
index a5f6059..46e8c54 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ClusterFeatureRepositoryRemoveCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ClusterFeatureRepositoryRemoveCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -30,7 +30,7 @@ import org.apache.karaf.shell.api.action.lifecycle.Service;
public class ClusterFeatureRepositoryRemoveCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "connection", description = "The connection to use", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ClusterFeatureUninstallCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ClusterFeatureUninstallCommand.java
similarity index 89%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ClusterFeatureUninstallCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ClusterFeatureUninstallCommand.java
index 2e44ad4..7998673 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ClusterFeatureUninstallCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ClusterFeatureUninstallCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -30,7 +30,7 @@ import org.apache.karaf.shell.api.action.lifecycle.Service;
public class ClusterFeatureUninstallCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "connection", description = "The connection to use", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ClusterGroupListCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ClusterGroupListCommand.java
similarity index 90%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ClusterGroupListCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ClusterGroupListCommand.java
index df00ffc..7fdf573 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ClusterGroupListCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ClusterGroupListCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -34,7 +34,7 @@ import java.util.Map;
public class ClusterGroupListCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "connection", description = "The connection to use", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ClusterNodeListCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ClusterNodeListCommand.java
similarity index 88%
copy from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ClusterNodeListCommand.java
copy to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ClusterNodeListCommand.java
index 78a6bbb..c98701b 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ClusterNodeListCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ClusterNodeListCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -32,7 +32,7 @@ import java.util.List;
public class ClusterNodeListCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "connection", description = "The connection to use", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConfigCreateCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConfigCreateCommand.java
similarity index 88%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConfigCreateCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConfigCreateCommand.java
index 86e895b..7d8523f 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConfigCreateCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConfigCreateCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -30,7 +30,7 @@ import org.apache.karaf.shell.api.action.lifecycle.Service;
public class ConfigCreateCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "connection", description = "The connection to use", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConfigDeleteCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConfigDeleteCommand.java
similarity index 88%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConfigDeleteCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConfigDeleteCommand.java
index a9c8704..ee47571 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConfigDeleteCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConfigDeleteCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -30,7 +30,7 @@ import org.apache.karaf.shell.api.action.lifecycle.Service;
public class ConfigDeleteCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "connection", description = "The connection to use", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConfigFactoryCreateCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConfigFactoryCreateCommand.java
similarity index 90%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConfigFactoryCreateCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConfigFactoryCreateCommand.java
index af2ac5c..bc6df2d 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConfigFactoryCreateCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConfigFactoryCreateCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -30,7 +30,7 @@ import org.apache.karaf.shell.api.action.lifecycle.Service;
public class ConfigFactoryCreateCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "connection", description = "The connection to use", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConfigListCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConfigListCommand.java
similarity index 88%
copy from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConfigListCommand.java
copy to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConfigListCommand.java
index 1284ebe..2987c59 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConfigListCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConfigListCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -32,7 +32,7 @@ import java.util.List;
public class ConfigListCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "connection", description = "The connection to use", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConfigPropertyAppendCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConfigPropertyAppendCommand.java
similarity index 90%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConfigPropertyAppendCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConfigPropertyAppendCommand.java
index ad575c8..51e02e8 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConfigPropertyAppendCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConfigPropertyAppendCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -30,7 +30,7 @@ import org.apache.karaf.shell.api.action.lifecycle.Service;
public class ConfigPropertyAppendCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "connection", description = "The connection to use", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConfigPropertyDeleteCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConfigPropertyDeleteCommand.java
similarity index 89%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConfigPropertyDeleteCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConfigPropertyDeleteCommand.java
index e7f084f..33b921a 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConfigPropertyDeleteCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConfigPropertyDeleteCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -30,7 +30,7 @@ import org.apache.karaf.shell.api.action.lifecycle.Service;
public class ConfigPropertyDeleteCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "connection", description = "The connection to use", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConfigPropertyListCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConfigPropertyListCommand.java
similarity index 89%
copy from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConfigPropertyListCommand.java
copy to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConfigPropertyListCommand.java
index e4f5ed0..14d8983 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConfigPropertyListCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConfigPropertyListCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -32,7 +32,7 @@ import java.util.Map;
public class ConfigPropertyListCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "connection", description = "The connection to use", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConfigPropertySetCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConfigPropertySetCommand.java
similarity index 90%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConfigPropertySetCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConfigPropertySetCommand.java
index 3e72cdc..95484ce 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConfigPropertySetCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConfigPropertySetCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -30,7 +30,7 @@ import org.apache.karaf.shell.api.action.lifecycle.Service;
public class ConfigPropertySetCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "connection", description = "The connection to use", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConnectionDeleteCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConnectionDeleteCommand.java
similarity index 87%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConnectionDeleteCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConnectionDeleteCommand.java
index 9c1b481..b559093 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConnectionDeleteCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConnectionDeleteCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -30,7 +30,7 @@ import org.apache.karaf.shell.api.action.lifecycle.Service;
public class ConnectionDeleteCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "connection", description = "The connection name", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConnectionListCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConnectionListCommand.java
similarity index 84%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConnectionListCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConnectionListCommand.java
index 2c110a7..ec628e2 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConnectionListCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConnectionListCommand.java
@@ -14,24 +14,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Connection;
-import org.apache.karaf.cave.deployer.api.Deployer;
+import org.apache.karaf.cave.deployer.Connection;
+import org.apache.karaf.cave.deployer.DeployerService;
import org.apache.karaf.shell.api.action.Action;
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 org.apache.karaf.shell.support.table.ShellTable;
-import java.util.Collections;
-
@Service
-@Command(scope = "cave", name = "deployer-connection", description = "List of registered connections in the deployer service")
+@Command(scope = "cave", name = "deployer-connection-list", description = "List of registered connections in the deployer service")
public class ConnectionListCommand implements Action {
@Reference
- Deployer deployer;
+ DeployerService deployer;
@Override
public Object execute() throws Exception {
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConnectionRegisterCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConnectionRegisterCommand.java
similarity index 92%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConnectionRegisterCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConnectionRegisterCommand.java
index 6907ee8..03d8d6d 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ConnectionRegisterCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ConnectionRegisterCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Connection;
-import org.apache.karaf.cave.deployer.api.Deployer;
+import org.apache.karaf.cave.deployer.Connection;
+import org.apache.karaf.cave.deployer.DeployerService;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -29,7 +29,7 @@ import org.apache.karaf.shell.api.action.lifecycle.Service;
public class ConnectionRegisterCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "name", description = "Name of the connection", required = true, multiValued = false)
String name;
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/DownloadCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/DownloadCommand.java
similarity index 91%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/DownloadCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/DownloadCommand.java
index 7dc48d5..863b598 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/DownloadCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/DownloadCommand.java
@@ -14,9 +14,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
+import org.apache.karaf.cave.deployer.DeployerService;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -28,7 +28,7 @@ import org.apache.karaf.shell.api.action.lifecycle.Service;
public class DownloadCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "artifact", description = "The artifact URL", required = true, multiValued = false)
String artifact;
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ExplodeCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ExplodeCommand.java
similarity index 92%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ExplodeCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ExplodeCommand.java
index 5dd9a7c..18ebef9 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ExplodeCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ExplodeCommand.java
@@ -14,9 +14,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
+import org.apache.karaf.cave.deployer.DeployerService;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -28,7 +28,7 @@ import org.apache.karaf.shell.api.action.lifecycle.Service;
public class ExplodeCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "url", description = "The location of the file", required = true, multiValued = false)
String url;
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ExtractCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ExtractCommand.java
similarity index 92%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ExtractCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ExtractCommand.java
index 2bb921f..8b0a3aa 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/ExtractCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/ExtractCommand.java
@@ -14,9 +14,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
+import org.apache.karaf.cave.deployer.DeployerService;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -28,7 +28,7 @@ import org.apache.karaf.shell.api.action.lifecycle.Service;
public class ExtractCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "url", description = "The location of the file", required = true, multiValued = false)
String url;
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/FeatureInstallCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/FeatureInstallCommand.java
similarity index 88%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/FeatureInstallCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/FeatureInstallCommand.java
index fe5d9be..56b89dc 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/FeatureInstallCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/FeatureInstallCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -30,7 +30,7 @@ import org.apache.karaf.shell.api.action.lifecycle.Service;
public class FeatureInstallCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "connection", description = "The connection to use", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/FeatureInstalledListCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/FeatureInstalledListCommand.java
similarity index 88%
copy from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/FeatureInstalledListCommand.java
copy to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/FeatureInstalledListCommand.java
index 4030a3a..2e8cd03 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/FeatureInstalledListCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/FeatureInstalledListCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -32,7 +32,7 @@ import java.util.List;
public class FeatureInstalledListCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "connection", description = "The connection to use", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/FeatureListCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/FeatureListCommand.java
similarity index 88%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/FeatureListCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/FeatureListCommand.java
index 1d1c181..4563bd0 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/FeatureListCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/FeatureListCommand.java
@@ -14,11 +14,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.api.Feature;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.Feature;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -34,7 +34,7 @@ import java.util.List;
public class FeatureListCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "connection", description = "The connection to use", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/FeatureUninstallCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/FeatureUninstallCommand.java
similarity index 88%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/FeatureUninstallCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/FeatureUninstallCommand.java
index b8c5a0b..395588b 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/FeatureUninstallCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/FeatureUninstallCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -30,7 +30,7 @@ import org.apache.karaf.shell.api.action.lifecycle.Service;
public class FeatureUninstallCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "connection", description = "The connection to use", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/FeaturesRepositoryAddCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/FeaturesRepositoryAddCommand.java
similarity index 88%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/FeaturesRepositoryAddCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/FeaturesRepositoryAddCommand.java
index 0e6b7a4..f5279f2 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/FeaturesRepositoryAddCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/FeaturesRepositoryAddCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -30,7 +30,7 @@ import org.apache.karaf.shell.api.action.lifecycle.Service;
public class FeaturesRepositoryAddCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "connection", description = "The connection to use", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/FeaturesRepositoryListCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/FeaturesRepositoryListCommand.java
similarity index 87%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/FeaturesRepositoryListCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/FeaturesRepositoryListCommand.java
index 0b24564..5802e89 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/FeaturesRepositoryListCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/FeaturesRepositoryListCommand.java
@@ -14,11 +14,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.api.FeaturesRepository;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.FeaturesRepository;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -34,7 +34,7 @@ import java.util.List;
public class FeaturesRepositoryListCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "connection", description = "The connection to use", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/FeaturesRepositoryProvideCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/FeaturesRepositoryProvideCommand.java
similarity index 91%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/FeaturesRepositoryProvideCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/FeaturesRepositoryProvideCommand.java
index 4b133e8..b16b1e2 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/FeaturesRepositoryProvideCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/FeaturesRepositoryProvideCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.api.Feature;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.Feature;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -32,7 +32,7 @@ import java.util.List;
public class FeaturesRepositoryProvideCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "featuresRepositoryUrl", description = "The location", required = true, multiValued = false)
String featuresRepositoryUrl;
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/FeaturesRepositoryRemoveCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/FeaturesRepositoryRemoveCommand.java
similarity index 88%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/FeaturesRepositoryRemoveCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/FeaturesRepositoryRemoveCommand.java
index 235540e..f4bab87 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/FeaturesRepositoryRemoveCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/FeaturesRepositoryRemoveCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -30,7 +30,7 @@ import org.apache.karaf.shell.api.action.lifecycle.Service;
public class FeaturesRepositoryRemoveCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "connection", description = "The connection to use", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/KarInstallCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/KarInstallCommand.java
similarity index 88%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/KarInstallCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/KarInstallCommand.java
index fd1ee6d..a768654 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/KarInstallCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/KarInstallCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -30,7 +30,7 @@ import org.apache.karaf.shell.api.action.lifecycle.Service;
public class KarInstallCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "connection", description = "The connection to use", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/KarListCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/KarListCommand.java
similarity index 88%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/KarListCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/KarListCommand.java
index a488d08..d3640c8 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/KarListCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/KarListCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -32,7 +32,7 @@ import java.util.List;
public class KarListCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "connection", description = "The connection to use", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/KarUninstallCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/KarUninstallCommand.java
similarity index 88%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/KarUninstallCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/KarUninstallCommand.java
index 5ba1525..423a6e1 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/KarUninstallCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/KarUninstallCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.service.command.completers.ConnectionCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -30,7 +30,7 @@ import org.apache.karaf.shell.api.action.lifecycle.Service;
public class KarUninstallCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Argument(index = 0, name = "connection", description = "The connection to use", required = true, multiValued = false)
@Completion(ConnectionCompleter.class)
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/UploadCommand.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/UploadCommand.java
similarity index 93%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/UploadCommand.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/UploadCommand.java
index da98973..7920f27 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/UploadCommand.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/UploadCommand.java
@@ -14,9 +14,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.deployer.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
+import org.apache.karaf.cave.deployer.DeployerService;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -29,7 +29,7 @@ import org.apache.karaf.shell.api.action.lifecycle.Service;
public class UploadCommand implements Action {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Option(name = "-g", aliases = "--groupId", description = "Maven groupId", required = true, multiValued = false)
String groupId;
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/completers/ConnectionCompleter.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/completers/ConnectionCompleter.java
similarity index 89%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/completers/ConnectionCompleter.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/completers/ConnectionCompleter.java
index 1a072e1..849f306 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/completers/ConnectionCompleter.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/command/completers/ConnectionCompleter.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command.completers;
+package org.apache.karaf.cave.deployer.service.command.completers;
-import org.apache.karaf.cave.deployer.api.Connection;
-import org.apache.karaf.cave.deployer.api.Deployer;
+import org.apache.karaf.cave.deployer.Connection;
+import org.apache.karaf.cave.deployer.DeployerService;
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;
@@ -31,7 +31,7 @@ import java.util.List;
public class ConnectionCompleter implements Completer {
@Reference
- private Deployer deployer;
+ private DeployerService deployer;
@Override
public int complete(Session session, CommandLine commandLine, List<String> list) {
diff --git a/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/impl/Activator.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/impl/Activator.java
deleted file mode 100644
index 732e811..0000000
--- a/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/impl/Activator.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.cave.deployer.service.impl;
-
-import org.apache.karaf.cave.deployer.api.Deployer;
-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;
-
-@Services(
- requires = { @RequireService(ConfigurationAdmin.class) },
- provides = { @ProvideService(Deployer.class) }
-)
-public class Activator extends BaseActivator {
-
- @Override
- public void doStart() {
- ConfigurationAdmin configurationAdmin = getTrackedService(ConfigurationAdmin.class);
- DeployerImpl deployer = new DeployerImpl();
- deployer.setConfigurationAdmin(configurationAdmin);
- register(Deployer.class, deployer);
- }
-
-}
diff --git a/deployer/management/src/main/java/org/apache/karaf/cave/deployer/management/CaveDeployerMBean.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/management/DeployerMBean.java
similarity index 97%
rename from deployer/management/src/main/java/org/apache/karaf/cave/deployer/management/CaveDeployerMBean.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/management/DeployerMBean.java
index ef9d37e..5e37d03 100644
--- a/deployer/management/src/main/java/org/apache/karaf/cave/deployer/management/CaveDeployerMBean.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/management/DeployerMBean.java
@@ -11,13 +11,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.management;
+package org.apache.karaf.cave.deployer.service.management;
import javax.management.openmbean.TabularData;
import java.util.List;
import java.util.Map;
-public interface CaveDeployerMBean {
+public interface DeployerMBean {
void registerConnection(String name, String jmxUrl, String karafName, String user, String password) throws Exception;
void deleteConnection(String name) throws Exception;
diff --git a/deployer/management/src/main/java/org/apache/karaf/cave/deployer/management/internal/CaveDeployerMBeanImpl.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/management/DeployerMBeanService.java
similarity index 94%
rename from deployer/management/src/main/java/org/apache/karaf/cave/deployer/management/internal/CaveDeployerMBeanImpl.java
rename to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/management/DeployerMBeanService.java
index 3f4db03..569e060 100644
--- a/deployer/management/src/main/java/org/apache/karaf/cave/deployer/management/internal/CaveDeployerMBeanImpl.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/management/DeployerMBeanService.java
@@ -11,10 +11,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.management.internal;
+package org.apache.karaf.cave.deployer.service.management;
-import org.apache.karaf.cave.deployer.api.*;
-import org.apache.karaf.cave.deployer.management.CaveDeployerMBean;
+import org.apache.karaf.cave.deployer.Bundle;
+import org.apache.karaf.cave.deployer.Connection;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.Feature;
+import org.apache.karaf.cave.deployer.FeaturesRepository;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
import javax.management.NotCompliantMBeanException;
import javax.management.StandardMBean;
@@ -22,23 +27,16 @@ import javax.management.openmbean.*;
import java.util.List;
import java.util.Map;
-public class CaveDeployerMBeanImpl extends StandardMBean implements CaveDeployerMBean {
+@Component(name = "org.apache.karaf.cave.deployer.management", immediate = true, property = { "jmx.objectname=org.apache.karaf.cave:type=deployer" })
+public class DeployerMBeanService extends StandardMBean implements DeployerMBean {
- private Deployer deployer;
+ @Reference
+ private DeployerService deployer;
- public CaveDeployerMBeanImpl() throws NotCompliantMBeanException {
- super(CaveDeployerMBean.class);
+ public DeployerMBeanService() throws NotCompliantMBeanException {
+ super(DeployerMBean.class);
}
- public Deployer getDeployer() {
- return deployer;
- }
-
- public void setDeployer(Deployer deployer) {
- this.deployer = deployer;
- }
-
-
@Override
public void registerConnection(String name, String jmxUrl, String karafName, String user, String password) throws Exception {
Connection connection = new Connection();
diff --git a/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/rest/DeployerRestApi.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/rest/DeployerRestApi.java
new file mode 100644
index 0000000..f909438
--- /dev/null
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/rest/DeployerRestApi.java
@@ -0,0 +1,253 @@
+/*
+ * 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.cave.deployer.service.rest;
+
+import org.apache.karaf.cave.deployer.Bundle;
+import org.apache.karaf.cave.deployer.Connection;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.apache.karaf.cave.deployer.Feature;
+import org.apache.karaf.cave.deployer.FeaturesRepository;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import java.util.List;
+import java.util.Map;
+
+@Path("/")
+public class DeployerRestApi {
+
+ private DeployerService deployerService;
+
+ public DeployerRestApi(DeployerService deployerService) {
+ this.deployerService = deployerService;
+ }
+
+ @GET
+ @Path("/connections")
+ @Produces("application/json")
+ List<Connection> getConnections() throws Exception {
+ return deployerService.connections();
+ }
+
+ @POST
+ @Path("/connections")
+ @Consumes("application/json")
+ public void addConnection(Connection connection) throws Exception {
+ deployerService.registerConnection(connection);
+ }
+
+ @DELETE
+ @Path("/connections/{connection}")
+ public void removeConnection(@PathParam(value = "connection") String connection) throws Exception {
+ deployerService.deleteConnection(connection);
+ }
+
+ @POST
+ @Path("/explode")
+ @Produces("application/json")
+ public List<String> explode(@HeaderParam(value = "url") String url, @HeaderParam(value = "repository") String repository) throws Exception {
+ return deployerService.explode(url, repository);
+ }
+
+ @POST
+ @Path("/extract")
+ public void extract(@HeaderParam(value = "url") String url, @HeaderParam(value = "directory") String directory) throws Exception {
+ deployerService.extract(url, directory);
+ }
+
+ @POST
+ @Path("/download")
+ public void download(@HeaderParam(value = "artifact") String artifact, @HeaderParam(value = "directory") String directory) throws Exception {
+ deployerService.download(artifact, directory);
+ }
+
+ @POST
+ @Path("/upload")
+ public void upload(@HeaderParam(value = "groupId") String groupId, @HeaderParam(value = "artifactId") String artifactId, @HeaderParam(value = "version") String version, @HeaderParam(value = "artifactUrl") String artifactUrl, @HeaderParam(value = "repositoryUrl") String repositoryUrl) throws Exception {
+ deployerService.upload(groupId, artifactId, version, artifactUrl, repositoryUrl);
+ }
+
+ @POST
+ @Path("/connections/{connection}/bundles")
+ public void installBundle(@HeaderParam(value = "artifactUrl") String artifactUrl, @PathParam(value = "connection") String connection) throws Exception {
+ deployerService.installBundle(artifactUrl, connection);
+ }
+
+ @DELETE
+ @Path("/connections/{connection}/bundles/{id}")
+ public void uninstallBundle(@PathParam(value = "id") String id, @PathParam(value = "connection") String connection) throws Exception {
+ deployerService.uninstallBundle(id, connection);
+ }
+
+ @POST
+ @Path("/connections/{connection}/bundles/{id}/start")
+ public void startBundle(@PathParam(value = "id") String id, @PathParam(value = "connection") String connection) throws Exception {
+ deployerService.startBundle(id, connection);
+ }
+
+ @POST
+ @Path("/connections/{connection}/bundles/{id}/stop")
+ public void stopBundle(@PathParam(value = "id") String id, @PathParam(value = "connection") String connection) throws Exception {
+ deployerService.stopBundle(id, connection);
+ }
+
+ @GET
+ @Path("/connections/{connection}/bundles")
+ @Produces("application/json")
+ public List<Bundle> listBundles(@PathParam(value = "connection") String connection) throws Exception {
+ return deployerService.bundles(connection);
+ }
+
+ @POST
+ @Path("/connections/{connection}/kars")
+ public void installKar(@HeaderParam(value = "artifactUrl") String artifactUrl, @PathParam(value = "connection") String connection) throws Exception {
+ deployerService.installKar(artifactUrl, connection);
+ }
+
+ @DELETE
+ @Path("/connections/{connection}/kars/{id}")
+ public void uninstallKar(@PathParam(value = "id") String id, @PathParam(value = "connection") String connection) throws Exception {
+ deployerService.uninstallKar(id, connection);
+ }
+
+ @GET
+ @Path("/connections/{connection}/kars")
+ public List<String> listKars(@PathParam(value = "connection") String connection) throws Exception {
+ return deployerService.kars(connection);
+ }
+
+ @GET
+ @Path("/features/repository")
+ public List<Feature> providedFeatures(@HeaderParam(value = "featuresRepositoryUrl") String featuresRepositoryUrl) throws Exception {
+ return deployerService.providedFeatures(featuresRepositoryUrl);
+ }
+
+ @GET
+ @Path("/connections/{connection}/features/repositories")
+ @Produces("application/json")
+ public List<FeaturesRepository> listFeaturesRepositories(@PathParam(value = "connection") String connection) throws Exception {
+ return deployerService.featuresRepositories(connection);
+ }
+
+ @POST
+ @Path("/connections/{connection}/features/repositories")
+ public void addFeaturesRepository(@HeaderParam(value = "artifactUrl") String artifactUrl, @PathParam(value = "connection") String connection) throws Exception {
+ deployerService.addFeaturesRepository(artifactUrl, connection);
+ }
+
+ @DELETE
+ @Path("/connections/{connection}/features/repositories")
+ public void removeFeaturesRepository(@HeaderParam(value = "artifactUrl") String artifactUrl, @PathParam(value = "connection") String connection) throws Exception {
+ deployerService.removeFeaturesRepository(artifactUrl, connection);
+ }
+
+ @GET
+ @Path("/connections/{connection}/features")
+ @Produces("application/json")
+ public List<Feature> listFeatures(@PathParam(value = "connection") String connection) throws Exception {
+ return deployerService.features(connection);
+ }
+
+ @POST
+ @Path("/connections/{connection}/features/{feature}")
+ public void installFeature(@PathParam(value = "feature") String feature, @PathParam(value = "connection") String connection) throws Exception {
+ deployerService.installFeature(feature, connection);
+ }
+
+ @DELETE
+ @Path("/connections/{connection}/features/{feature}")
+ public void uninstallFeature(@PathParam(value = "feature") String feature, @PathParam(value = "connection") String connection) throws Exception {
+ deployerService.uninstallFeature(feature, connection);
+ }
+
+ @GET
+ @Path("/connections/{connection}/configurations")
+ @Produces("application/json")
+ public List<String> getConfigs(@PathParam(value = "connection") String connection) throws Exception {
+ return deployerService.configs(connection);
+ }
+
+ @POST
+ @Path("/connections/{connection}/configurations/{pid}")
+ public void createconfig(@PathParam(value = "pid") String pid, @PathParam(value = "connection") String connection) throws Exception {
+ deployerService.createConfig(pid, connection);
+ }
+
+ @POST
+ @Path("/connections/{connection}/configurations/factories/{factoryPid}")
+ public void createConfigFactory(@PathParam(value = "factoryPid") String pid, @PathParam(value = "connection") String connection) throws Exception {
+ deployerService.createConfigurationFactory(pid, connection);
+ }
+
+ @GET
+ @Path("/connections/{connection}/configurations/{pid}/properties")
+ @Produces("application/json")
+ public Map<String, String> getConfigProperties(@PathParam(value = "pid")String pid, @PathParam(value = "connection") String connection) throws Exception {
+ return deployerService.configProperties(pid, connection);
+ }
+
+ @DELETE
+ @Path("/connections/{connection}/configurations/{pid}")
+ public void deleteConfig(@PathParam(value = "pid") String pid, @PathParam(value = "connection") String connection) throws Exception {
+ deployerService.deleteConfig(pid, connection);
+ }
+
+ @GET
+ @Path("/connections/{connection}/cluster/nodes")
+ @Produces("application/json")
+ public List<String> listClusterNodes(@PathParam(value = "connection") String connection) throws Exception {
+ return deployerService.clusterNodes(connection);
+ }
+
+ @GET
+ @Path("/connections/{connection}/cluster/groups")
+ @Produces("application/json")
+ public Map<String, List<String>> listClusterGroups(@PathParam(value = "connection") String connection) throws Exception {
+ return deployerService.clusterGroups(connection);
+ }
+
+ @POST
+ @Path("/connections/{connection}/cluster/groups/{group}/features/repositories")
+ public void clusterAddFeaturesRepository(@PathParam(value = "connection") String connection, @PathParam(value = "group") String clusterGroup, @HeaderParam(value = "url") String repositoryUrl) throws Exception {
+ deployerService.clusterAddFeaturesRepository(repositoryUrl, clusterGroup, connection);
+ }
+
+ @DELETE
+ @Path("/connections/{connection}/cluster/groups/{group}/features/repositories")
+ public void clusterRemoveFeaturesRepository(@PathParam(value = "connection") String connection, @PathParam(value = "group") String clusterGroup, @HeaderParam(value = "url") String repositoryUrl) throws Exception {
+ deployerService.clusterRemoveFeaturesRepository(repositoryUrl, clusterGroup, connection);
+ }
+
+ @POST
+ @Path("/connections/{connection}/cluster/groups/{group}/features/{feature}")
+ public void clusterInstallFeature(@PathParam(value = "connection") String connection, @PathParam(value = "group") String clusterGroup, @PathParam(value = "feature") String feature) throws Exception {
+ deployerService.clusterFeatureInstall(feature, clusterGroup, connection);
+ }
+
+ @DELETE
+ @Path("/connections/{connection}/cluster/groups/{group}/features/{feature}")
+ public void clusterUninstallFeature(@PathParam(value = "connection") String connection, @PathParam(value = "group") String clusterGroup, @PathParam(value = "feature") String feature) throws Exception {
+ deployerService.clusterFeatureUninstall(feature, clusterGroup, connection);
+ }
+
+}
diff --git a/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/rest/DeployerRestService.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/rest/DeployerRestService.java
new file mode 100644
index 0000000..0fe5ec0
--- /dev/null
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/rest/DeployerRestService.java
@@ -0,0 +1,69 @@
+/*
+ * 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.cave.deployer.service.rest;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.bus.extension.ExtensionManagerBus;
+import org.apache.cxf.transport.http.DestinationRegistry;
+import org.apache.cxf.transport.http.DestinationRegistryImpl;
+import org.apache.cxf.transport.http.HTTPTransportFactory;
+import org.apache.karaf.cave.deployer.DeployerService;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.http.HttpService;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Component(name = "org.apache.karaf.cave.deployer.rest")
+public class DeployerRestService {
+
+ @Reference
+ private DeployerService deployerService;
+
+ @Reference
+ private HttpService httpService;
+
+ @Activate
+ public void activate(ComponentContext componentContext) throws Exception {
+ DeployerRestApi restApi = new DeployerRestApi(deployerService);
+
+ Map<Class<?>, Object> extensions = new HashMap<>();
+ DestinationRegistry destinationRegistry = new DestinationRegistryImpl();
+ HTTPTransportFactory httpTransportFactory = new HTTPTransportFactory(destinationRegistry);
+ extensions.put(HTTPTransportFactory.class, httpTransportFactory);
+ extensions.put(DestinationRegistry.class, destinationRegistry);
+ Bus bus = new ExtensionManagerBus(extensions, null, getClass().getClassLoader());
+ org.apache.cxf.transport.DestinationFactoryManager destinationFactoryManager = bus.getExtension(org.apache.cxf.transport.DestinationFactoryManager.class);
+ for (String url : HTTPTransportFactory.DEFAULT_NAMESPACES) {
+ destinationFactoryManager.registerDestinationFactory(url, httpTransportFactory);
+ }
+
+ DeployerRestServlet deployerRestServlet = new DeployerRestServlet(restApi, destinationRegistry, bus);
+
+ httpService.registerServlet("/cave/deployer/api", deployerRestServlet, null, null);
+ }
+
+ @Deactivate
+ public void deactivate() throws Exception {
+ httpService.unregister("/cave/deployer/api");
+ }
+
+}
diff --git a/rest/src/main/java/org/apache/karaf/cave/rest/CaveRestServlet.java b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/rest/DeployerRestServlet.java
similarity index 51%
copy from rest/src/main/java/org/apache/karaf/cave/rest/CaveRestServlet.java
copy to deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/rest/DeployerRestServlet.java
index 6801cfe..6f63e30 100644
--- a/rest/src/main/java/org/apache/karaf/cave/rest/CaveRestServlet.java
+++ b/deployer/service/src/main/java/org/apache/karaf/cave/deployer/service/rest/DeployerRestServlet.java
@@ -14,47 +14,39 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.rest;
+package org.apache.karaf.cave.deployer.service.rest;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
+import org.apache.cxf.Bus;
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
+import org.apache.cxf.jaxrs.JAXRSServiceFactoryBean;
+import org.apache.cxf.transport.http.DestinationRegistry;
import org.apache.cxf.transport.servlet.CXFNonSpringServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
-public class CaveRestServlet extends CXFNonSpringServlet {
+public class DeployerRestServlet extends CXFNonSpringServlet {
- private RepositoryRest repositoryRest;
- private DeployerRest deployerRest;
+ private DeployerRestApi restApi;
- public CaveRestServlet(RepositoryRest repositoryRest, DeployerRest deployerRest) {
- this.repositoryRest = repositoryRest;
- this.deployerRest = deployerRest;
+ public DeployerRestServlet(DeployerRestApi restApi, DestinationRegistry destinationRegistry, Bus bus) {
+ super(destinationRegistry, false);
+ this.restApi = restApi;
+ this.setBus(bus);
}
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
-
- if (repositoryRest != null) {
- JAXRSServerFactoryBean repositoryBean = new JAXRSServerFactoryBean();
- repositoryBean.setAddress("/repository");
- repositoryBean.setBus(getBus());
- repositoryBean.setProvider(new JacksonJsonProvider());
- repositoryBean.setServiceBean(repositoryRest);
- repositoryBean.create();
- }
-
- if (deployerRest != null) {
- JAXRSServerFactoryBean deployerBean = new JAXRSServerFactoryBean();
- deployerBean.setAddress("/deployer");
- deployerBean.setBus(getBus());
- deployerBean.setProvider(new JacksonJsonProvider());
- deployerBean.setServiceBean(deployerRest);
- deployerBean.create();
+ if (restApi != null) {
+ JAXRSServerFactoryBean bean = new JAXRSServerFactoryBean();
+ bean.setAddress("/");
+ bean.setBus(getBus());
+ bean.setProvider(new JacksonJsonProvider());
+ bean.setServiceBean(restApi);
+ bean.create();
}
-
}
}
diff --git a/deployer/service/src/test/java/org/apache/karaf/cave/deployer/service/impl/DeployerImplTest.java b/deployer/service/src/test/java/org/apache/karaf/cave/deployer/service/DeployerImplTest.java
similarity index 93%
rename from deployer/service/src/test/java/org/apache/karaf/cave/deployer/service/impl/DeployerImplTest.java
rename to deployer/service/src/test/java/org/apache/karaf/cave/deployer/service/DeployerImplTest.java
index 6b89571..02a792c 100644
--- a/deployer/service/src/test/java/org/apache/karaf/cave/deployer/service/impl/DeployerImplTest.java
+++ b/deployer/service/src/test/java/org/apache/karaf/cave/deployer/service/DeployerImplTest.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.service.impl;
+package org.apache.karaf.cave.deployer.service;
-import org.apache.karaf.cave.deployer.api.Config;
-import org.apache.karaf.cave.deployer.api.Deployer;
+import org.apache.karaf.cave.deployer.Config;
+import org.apache.karaf.cave.deployer.DeployerService;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -28,18 +28,17 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
public class DeployerImplTest {
- private Deployer deployer;
+ private DeployerService deployer;
@Before
public void startup() {
System.setProperty("java.protocol.handler.pkgs", "org.ops4j.pax.url");
System.setProperty("java.io.tmpdir", "target");
- deployer = new DeployerImpl();
+ deployer = new DeployerServiceImpl();
}
@Test
@@ -117,7 +116,7 @@ public class DeployerImplTest {
@Test
public void mvnParseTest() throws Exception {
String mvnUrl = "mvn:testGroupId/testArtifactId/1.0";
- Map<String, String> coordonates = DeployerImpl.parse(mvnUrl);
+ Map<String, String> coordonates = DeployerServiceImpl.parse(mvnUrl);
Assert.assertEquals("testGroupId", coordonates.get("groupId"));
Assert.assertEquals("testArtifactId", coordonates.get("artifactId"));
Assert.assertEquals("1.0", coordonates.get("version"));
@@ -125,7 +124,7 @@ public class DeployerImplTest {
Assert.assertNull(coordonates.get("classifier"));
mvnUrl = "mvn:testGroupId/testArtifactId/1.0/kar";
- coordonates = DeployerImpl.parse(mvnUrl);
+ coordonates = DeployerServiceImpl.parse(mvnUrl);
Assert.assertEquals("testGroupId", coordonates.get("groupId"));
Assert.assertEquals("testArtifactId", coordonates.get("artifactId"));
Assert.assertEquals("1.0", coordonates.get("version"));
@@ -133,7 +132,7 @@ public class DeployerImplTest {
Assert.assertNull(coordonates.get("classifier"));
mvnUrl = "mvn:testGroupId/testArtifactId/1.0/xml/features";
- coordonates = DeployerImpl.parse(mvnUrl);
+ coordonates = DeployerServiceImpl.parse(mvnUrl);
Assert.assertEquals("testGroupId", coordonates.get("groupId"));
Assert.assertEquals("testArtifactId", coordonates.get("artifactId"));
Assert.assertEquals("1.0", coordonates.get("version"));
diff --git a/server/api/pom.xml b/gateway/api/pom.xml
similarity index 78%
rename from server/api/pom.xml
rename to gateway/api/pom.xml
index d10d12a..a3c1688 100644
--- a/server/api/pom.xml
+++ b/gateway/api/pom.xml
@@ -23,29 +23,27 @@
<parent>
<groupId>org.apache.karaf.cave</groupId>
- <artifactId>org.apache.karaf.cave.server</artifactId>
- <version>4.1.3-SNAPSHOT</version>
+ <artifactId>org.apache.karaf.cave.gateway</artifactId>
+ <version>4.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
- <groupId>org.apache.karaf.cave.server</groupId>
- <artifactId>org.apache.karaf.cave.server.api</artifactId>
- <name>Apache Karaf :: Cave :: Server :: API</name>
+ <groupId>org.apache.karaf.cave.gateway</groupId>
+ <artifactId>org.apache.karaf.cave.gateway.api</artifactId>
+ <name>Apache Karaf :: Cave :: Features Gateway :: API</name>
<packaging>bundle</packaging>
- <dependencies>
- </dependencies>
-
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
+ <inherited>true</inherited>
+ <extensions>true</extensions>
<configuration>
<instructions>
- <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
<Export-Package>
- org.apache.karaf.cave.server.api
+ org.apache.karaf.cave.gateway
</Export-Package>
</instructions>
</configuration>
@@ -53,4 +51,4 @@
</plugins>
</build>
-</project>
+</project>
\ No newline at end of file
diff --git a/server/api/src/main/java/org/apache/karaf/cave/server/api/CaveFeatureGateway.java b/gateway/api/src/main/java/org/apache/karaf/cave/gateway/FeaturesGatewayService.java
similarity index 64%
rename from server/api/src/main/java/org/apache/karaf/cave/server/api/CaveFeatureGateway.java
rename to gateway/api/src/main/java/org/apache/karaf/cave/gateway/FeaturesGatewayService.java
index 196dfe0..b451f6e 100644
--- a/server/api/src/main/java/org/apache/karaf/cave/server/api/CaveFeatureGateway.java
+++ b/gateway/api/src/main/java/org/apache/karaf/cave/gateway/FeaturesGatewayService.java
@@ -14,19 +14,34 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.server.api;
+package org.apache.karaf.cave.gateway;
-import java.io.File;
import java.util.List;
-public interface CaveFeatureGateway {
-
- String STORAGE = System.getProperty("karaf.data") + File.separator + "cave-features-gateway.xml";
+/**
+ * Manage features gateway.
+ */
+public interface FeaturesGatewayService {
+ /**
+ * Register a new features repository in the gateway.
+ *
+ * @param url the features repository XML URL.
+ */
void register(String url) throws Exception;
+ /**
+ * Remove a features repository from the gateway.
+ *
+ * @param id the features repository name or URL.
+ */
void remove(String id) throws Exception;
+ /**
+ * List the features repositories registered in the gateway.
+ *
+ * @return the features repositories registered in the gateway.
+ */
List<String> list() throws Exception;
}
diff --git a/server/pom.xml b/gateway/pom.xml
similarity index 81%
rename from server/pom.xml
rename to gateway/pom.xml
index 6b2f813..7841e34 100644
--- a/server/pom.xml
+++ b/gateway/pom.xml
@@ -24,22 +24,18 @@
<parent>
<groupId>org.apache.karaf</groupId>
<artifactId>cave</artifactId>
- <version>4.1.3-SNAPSHOT</version>
+ <version>4.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>org.apache.karaf.cave</groupId>
- <artifactId>org.apache.karaf.cave.server</artifactId>
- <name>Apache Karaf :: Cave :: Server</name>
+ <artifactId>org.apache.karaf.cave.gateway</artifactId>
+ <name>Apache Karaf :: Cave :: Features Gateway</name>
<packaging>pom</packaging>
<modules>
<module>api</module>
- <module>storage</module>
- <module>management</module>
- <module>command</module>
- <module>http</module>
- <module>maven</module>
+ <module>service</module>
</modules>
-</project>
+</project>
\ No newline at end of file
diff --git a/server/maven/pom.xml b/gateway/service/pom.xml
similarity index 62%
rename from server/maven/pom.xml
rename to gateway/service/pom.xml
index 4eb8ed4..dd851a7 100644
--- a/server/maven/pom.xml
+++ b/gateway/service/pom.xml
@@ -23,92 +23,79 @@
<parent>
<groupId>org.apache.karaf.cave</groupId>
- <artifactId>org.apache.karaf.cave.server</artifactId>
- <version>4.1.3-SNAPSHOT</version>
+ <artifactId>org.apache.karaf.cave.gateway</artifactId>
+ <version>4.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
- <groupId>org.apache.karaf.cave.server</groupId>
- <artifactId>org.apache.karaf.cave.server.maven</artifactId>
+ <groupId>org.apache.karaf.cave.gateway</groupId>
+ <artifactId>org.apache.karaf.cave.gateway.service</artifactId>
+ <name>Apache Karaf :: Cave :: Features Gateway :: Service</name>
<packaging>bundle</packaging>
- <name>Apache Karaf :: Cave :: Server :: Maven Proxy</name>
- <description>Maven Proxy Service</description>
-
- <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>
- <Import-Package>
- javax.servlet.*;version="[3.0,4)",
- !shaded.*,
- *
- </Import-Package>
- <Export-Package>
- !*
- </Export-Package>
- <Private-Package>
- org.apache.karaf.cave.server.maven,
- org.apache.karaf.util,
- org.apache.karaf.util.base64
- </Private-Package>
- </instructions>
- </configuration>
- </plugin>
- </plugins>
- </build>
<dependencies>
<dependency>
- <groupId>org.apache.karaf.cave.server</groupId>
- <artifactId>org.apache.karaf.cave.server.api</artifactId>
- </dependency>
- <dependency>
- <groupId>${servlet.spec.groupId}</groupId>
- <artifactId>${servlet.spec.artifactId}</artifactId>
+ <groupId>org.apache.karaf.cave.gateway</groupId>
+ <artifactId>org.apache.karaf.cave.gateway.api</artifactId>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>osgi.core</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>osgi.cmpn</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
- <groupId>org.ops4j.pax.url</groupId>
- <artifactId>pax-url-aether</artifactId>
- <scope>provided</scope>
+ <groupId>org.apache.karaf.features</groupId>
+ <artifactId>org.apache.karaf.features.core</artifactId>
</dependency>
<dependency>
- <groupId>org.apache.karaf</groupId>
- <artifactId>org.apache.karaf.util</artifactId>
+ <groupId>${servlet.spec.groupId}</groupId>
+ <artifactId>${servlet.spec.artifactId}</artifactId>
</dependency>
-
- <!-- Test Dependencies -->
<dependency>
- <groupId>org.ops4j.pax.web</groupId>
- <artifactId>pax-web-jetty-bundle</artifactId>
- <scope>test</scope>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.core</artifactId>
</dependency>
<dependency>
- <groupId>org.easymock</groupId>
- <artifactId>easymock</artifactId>
- <scope>test</scope>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-frontend-jaxrs</artifactId>
</dependency>
<dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-log4j12</artifactId>
- <scope>test</scope>
+ <groupId>com.fasterxml.jackson.jaxrs</groupId>
+ <artifactId>jackson-jaxrs-json-provider</artifactId>
</dependency>
</dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <inherited>true</inherited>
+ <configuration>
+ <instructions>
+ <Export-Package>
+ !*
+ </Export-Package>
+ <Import-Package>
+ com.fasterxml.jackson*;version="[2.8,3)",
+ *
+ </Import-Package>
+ <Private-Package>
+ org.apache.karaf.cave.gateway.service*,
+ org.apache.karaf.features.internal.model,
+ org.apache.karaf.features.internal.util,
+ org.apache.karaf.util,
+ org.apache.felix.utils*
+ </Private-Package>
+ <_dsannotations>*</_dsannotations>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
</project>
\ No newline at end of file
diff --git a/gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/FeaturesGatewayServiceImpl.java b/gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/FeaturesGatewayServiceImpl.java
new file mode 100644
index 0000000..58a61cb
--- /dev/null
+++ b/gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/FeaturesGatewayServiceImpl.java
@@ -0,0 +1,132 @@
+/*
+ * 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.cave.gateway.service;
+
+import org.apache.karaf.cave.gateway.FeaturesGatewayService;
+import org.apache.karaf.cave.gateway.service.http.FeaturesGatewayServlet;
+import org.apache.karaf.features.internal.model.Features;
+import org.apache.karaf.features.internal.model.JaxbUtil;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.http.HttpService;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.List;
+
+@Component(name = "org.apache.karaf.cave.gateway", immediate = true, service = FeaturesGatewayService.class)
+public class FeaturesGatewayServiceImpl implements FeaturesGatewayService {
+
+ @Reference
+ private HttpService httpService;
+
+ private String storage;
+ private String alias;
+
+ @Activate
+ public void activate(ComponentContext componentContext) throws Exception {
+ activate(componentContext.getProperties());
+ }
+
+ public void activate(Dictionary<String, Object> properties) throws Exception {
+ storage = (properties.get("storage.location") != null) ? properties.get("storage.location").toString() : System.getProperty("karaf.data") + File.separator + "cave" + File.separator + "features-gateway.xml";
+ alias = (properties.get("http.alias") != null) ? properties.get("http.alias").toString() : "/cave/features-gateway-repository";
+ FeaturesGatewayServlet featuresGatewayServlet = new FeaturesGatewayServlet(storage);
+ httpService.registerServlet(alias, featuresGatewayServlet, null, null);
+ }
+
+ @Deactivate
+ public void deactivate() {
+ httpService.unregister(alias);
+ }
+
+ @Override
+ public void register(String url) throws Exception {
+ File store = new File(storage);
+
+ Features features = new Features();
+ if (store.exists()) {
+ features = JaxbUtil.unmarshal(store.getAbsolutePath(), true);
+ } else {
+ store.getParentFile().mkdirs();
+ store.createNewFile();
+ }
+ features.setName("cave-features-gateway");
+ if (isFeaturesRepositoryRegistered(url, features.getRepository())) {
+ throw new IllegalArgumentException("Features repository " + url + " already registered in the gateway");
+ }
+ features.getRepository().add(url);
+
+ try (FileOutputStream fos = new FileOutputStream(store)) {
+ JaxbUtil.marshal(features, fos);
+ }
+ }
+
+ @Override
+ public void remove(String url) throws Exception {
+ File store = new File(storage);
+
+ if (!store.exists()) {
+ return;
+ }
+
+ Features features = JaxbUtil.unmarshal(store.getAbsolutePath(), true);
+ if (!isFeaturesRepositoryRegistered(url, features.getRepository())) {
+ throw new IllegalArgumentException("Features repository " + url + " is not registered in the gateway");
+ }
+ features.getRepository().remove(url);
+
+ try (FileOutputStream fos = new FileOutputStream(store)) {
+ JaxbUtil.marshal(features, fos);
+ }
+ }
+
+ @Override
+ public List<String> list() throws Exception {
+ List<String> featuresRepositories = new ArrayList<>();
+
+ File store = new File(storage);
+ if (store.exists()) {
+ Features features = JaxbUtil.unmarshal(store.getAbsolutePath(), true);
+ featuresRepositories = features.getRepository();
+ }
+
+ return featuresRepositories;
+ }
+
+ private boolean isFeaturesRepositoryRegistered(String url, List<String> repositories) {
+ for (String repository : repositories) {
+ if (repository.equals(url)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Only visible for testing.
+ */
+ protected void setHttpService(HttpService httpService) {
+ this.httpService = httpService;
+ }
+
+}
diff --git a/server/command/src/main/java/org/apache/karaf/cave/server/command/GatewayListCommand.java b/gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/command/FeaturesGatewayListCommand.java
similarity index 73%
rename from server/command/src/main/java/org/apache/karaf/cave/server/command/GatewayListCommand.java
rename to gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/command/FeaturesGatewayListCommand.java
index 07c34b3..3d3f7da 100644
--- a/server/command/src/main/java/org/apache/karaf/cave/server/command/GatewayListCommand.java
+++ b/gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/command/FeaturesGatewayListCommand.java
@@ -14,24 +14,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.server.command;
+package org.apache.karaf.cave.gateway.service.command;
-import org.apache.karaf.cave.server.api.CaveFeatureGateway;
+import org.apache.karaf.cave.gateway.FeaturesGatewayService;
import org.apache.karaf.shell.api.action.Action;
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;
@Service
-@Command(scope = "cave", name = "gateway-list", description = "List the features repositories registered in the gateway")
-public class GatewayListCommand implements Action {
+@Command(scope = "cave", name = "features-gateway-list", description = "List the features repositories registered in the gateway")
+public class FeaturesGatewayListCommand implements Action {
@Reference
- private CaveFeatureGateway gateway;
+ private FeaturesGatewayService featuresGatewayService;
@Override
public Object execute() throws Exception {
- for (String repository : gateway.list()) {
+ for (String repository : featuresGatewayService.list()) {
System.out.println(repository);
}
return null;
diff --git a/server/command/src/main/java/org/apache/karaf/cave/server/command/GatewayRegisterCommand.java b/gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/command/FeaturesGatewayRegisterCommand.java
similarity index 64%
rename from server/command/src/main/java/org/apache/karaf/cave/server/command/GatewayRegisterCommand.java
rename to gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/command/FeaturesGatewayRegisterCommand.java
index 86c5f70..0dea576 100644
--- a/server/command/src/main/java/org/apache/karaf/cave/server/command/GatewayRegisterCommand.java
+++ b/gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/command/FeaturesGatewayRegisterCommand.java
@@ -14,28 +14,32 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.server.command;
+package org.apache.karaf.cave.gateway.service.command;
-import org.apache.karaf.cave.server.api.CaveFeatureGateway;
+import org.apache.karaf.cave.gateway.FeaturesGatewayService;
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.lifecycle.Reference;
import org.apache.karaf.shell.api.action.lifecycle.Service;
-@Service
-@Command(scope = "cave", name = "gateway-register", description = "Register a features repository in the gateway")
-public class GatewayRegisterCommand implements Action {
+import java.util.List;
- @Argument(name = "repository", description = "The features repository URL ", required = true, multiValued = false)
- String repository;
+@Service
+@Command(scope = "cave", name = "features-gateway-register", description = "Register features repository URLs in the gateway")
+public class FeaturesGatewayRegisterCommand implements Action {
@Reference
- CaveFeatureGateway gateway;
+ private FeaturesGatewayService featuresGatewayService;
+
+ @Argument(index = 0, name = "urls", description = "The features repository URLs to register in the gateway", required = true, multiValued = true)
+ List<String> urls;
@Override
public Object execute() throws Exception {
- gateway.register(repository);
+ for (String url : urls) {
+ featuresGatewayService.register(url);
+ }
return null;
}
diff --git a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/FeatureInstalledListCommand.java b/gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/command/FeaturesGatewayRemoveCommand.java
similarity index 62%
rename from deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/FeatureInstalledListCommand.java
rename to gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/command/FeaturesGatewayRemoveCommand.java
index 4030a3a..981e5da 100644
--- a/deployer/command/src/main/java/org/apache/karaf/cave/deployer/command/FeatureInstalledListCommand.java
+++ b/gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/command/FeaturesGatewayRemoveCommand.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.deployer.command;
+package org.apache.karaf.cave.gateway.service.command;
-import org.apache.karaf.cave.deployer.api.Deployer;
-import org.apache.karaf.cave.deployer.command.completers.ConnectionCompleter;
+import org.apache.karaf.cave.gateway.FeaturesGatewayService;
+import org.apache.karaf.cave.gateway.service.command.completers.FeaturesRepositoryUrlCompleter;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
@@ -28,21 +28,20 @@ import org.apache.karaf.shell.api.action.lifecycle.Service;
import java.util.List;
@Service
-@Command(scope = "cave", name = "deployer-feature-installed-list", description = "List the features installed on a remote host")
-public class FeatureInstalledListCommand implements Action {
+@Command(scope = "cave", name = "features-gateway-remove", description = "Remove features repository XML URLs from the gateway")
+public class FeaturesGatewayRemoveCommand implements Action {
@Reference
- private Deployer deployer;
+ private FeaturesGatewayService featuresGatewayService;
- @Argument(index = 0, name = "connection", description = "The connection to use", required = true, multiValued = false)
- @Completion(ConnectionCompleter.class)
- String connection;
+ @Argument(index = 0, name = "urls", description = "The features repository URLs to remove from the gateway", required = true, multiValued = true)
+ @Completion(FeaturesRepositoryUrlCompleter.class)
+ List<String> urls;
@Override
public Object execute() throws Exception {
- List<String> features = deployer.installedFeatures(connection);
- for (String feature : features) {
- System.out.println(feature);
+ for (String url : urls) {
+ featuresGatewayService.remove(url);
}
return null;
}
diff --git a/server/command/src/main/java/org/apache/karaf/cave/server/command/completers/GatewayCompleter.java b/gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/command/completers/FeaturesRepositoryUrlCompleter.java
similarity index 81%
rename from server/command/src/main/java/org/apache/karaf/cave/server/command/completers/GatewayCompleter.java
rename to gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/command/completers/FeaturesRepositoryUrlCompleter.java
index 7e85a37..442a7d6 100644
--- a/server/command/src/main/java/org/apache/karaf/cave/server/command/completers/GatewayCompleter.java
+++ b/gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/command/completers/FeaturesRepositoryUrlCompleter.java
@@ -14,9 +14,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.server.command.completers;
+package org.apache.karaf.cave.gateway.service.command.completers;
-import org.apache.karaf.cave.server.api.CaveFeatureGateway;
+import org.apache.karaf.cave.gateway.FeaturesGatewayService;
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;
@@ -27,20 +27,20 @@ import org.apache.karaf.shell.support.completers.StringsCompleter;
import java.util.List;
@Service
-public class GatewayCompleter implements Completer {
+public class FeaturesRepositoryUrlCompleter implements Completer {
@Reference
- private CaveFeatureGateway gateway;
+ private FeaturesGatewayService featuresGatewayService;
@Override
public int complete(Session session, CommandLine commandLine, List<String> list) {
StringsCompleter delegate = new StringsCompleter();
try {
- for (String repository : gateway.list()) {
+ for (String repository : featuresGatewayService.list()) {
delegate.getStrings().add(repository);
}
} catch (Exception e) {
- // ignore
+ // nothing to do
}
return delegate.complete(session, commandLine, list);
}
diff --git a/gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/http/FeaturesGatewayServlet.java b/gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/http/FeaturesGatewayServlet.java
new file mode 100644
index 0000000..8d95c11
--- /dev/null
+++ b/gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/http/FeaturesGatewayServlet.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.cave.gateway.service.http;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+public class FeaturesGatewayServlet extends HttpServlet {
+
+ private String storage;
+
+ public FeaturesGatewayServlet(String storage) {
+ this.storage = storage;
+ }
+
+ @Override
+ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ response.setContentType("application/xml");
+ File store = new File(storage);
+ if (store.exists()) {
+ try (BufferedReader reader = new BufferedReader(new FileReader(store))) {
+ try (PrintWriter writer = response.getWriter()) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ writer.println(line);
+ }
+ writer.flush();
+ }
+ }
+ }
+ }
+
+}
diff --git a/server/management/src/main/java/org/apache/karaf/cave/server/management/CaveGatewayMBean.java b/gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/management/FeaturesGatewayMBean.java
similarity index 70%
rename from server/management/src/main/java/org/apache/karaf/cave/server/management/CaveGatewayMBean.java
rename to gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/management/FeaturesGatewayMBean.java
index fdca603..9f6da48 100644
--- a/server/management/src/main/java/org/apache/karaf/cave/server/management/CaveGatewayMBean.java
+++ b/gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/management/FeaturesGatewayMBean.java
@@ -11,14 +11,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.server.management;
+package org.apache.karaf.cave.gateway.service.management;
import java.util.List;
-public interface CaveGatewayMBean {
+public interface FeaturesGatewayMBean {
- List<String> list() throws Exception;
- void register(String repository) throws Exception;
- void remove(String repository) throws Exception;
+ List<String> getRepositories() throws Exception;
+
+ void register(String url) throws Exception;
+ void remove(String url) throws Exception;
}
diff --git a/gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/management/FeaturesGatewayMBeanService.java b/gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/management/FeaturesGatewayMBeanService.java
new file mode 100644
index 0000000..2488202
--- /dev/null
+++ b/gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/management/FeaturesGatewayMBeanService.java
@@ -0,0 +1,48 @@
+/*
+ * 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.cave.gateway.service.management;
+
+import org.apache.karaf.cave.gateway.FeaturesGatewayService;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+import javax.management.NotCompliantMBeanException;
+import javax.management.StandardMBean;
+import java.util.List;
+
+@Component(name = "org.apache.karaf.cave.gateway.management", property = { "jmx.objectname=org.apache.karaf.cave:type=gateway" })
+public class FeaturesGatewayMBeanService extends StandardMBean implements FeaturesGatewayMBean {
+
+ @Reference
+ private FeaturesGatewayService featuresGatewayService;
+
+ public FeaturesGatewayMBeanService() throws NotCompliantMBeanException {
+ super(FeaturesGatewayMBean.class);
+ }
+
+ @Override
+ public List<String> getRepositories() throws Exception {
+ return featuresGatewayService.list();
+ }
+
+ @Override
+ public void register(String url) throws Exception {
+ featuresGatewayService.register(url);
+ }
+
+ @Override
+ public void remove(String url) throws Exception {
+ featuresGatewayService.remove(url);
+ }
+}
diff --git a/gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/rest/FeaturesGatewayRestApi.java b/gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/rest/FeaturesGatewayRestApi.java
new file mode 100644
index 0000000..55bd29f
--- /dev/null
+++ b/gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/rest/FeaturesGatewayRestApi.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.cave.gateway.service.rest;
+
+import org.apache.karaf.cave.gateway.FeaturesGatewayService;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import java.util.List;
+
+@Path("/")
+public class FeaturesGatewayRestApi {
+
+ private FeaturesGatewayService featuresGatewayService;
+
+ public FeaturesGatewayRestApi(FeaturesGatewayService featuresGatewayService) {
+ this.featuresGatewayService = featuresGatewayService;
+ }
+
+ @GET
+ @Path("/")
+ @Produces("application/json")
+ public List<String> getFeaturesRepositories() throws Exception {
+ return featuresGatewayService.list();
+ }
+
+ @POST
+ @Path("/")
+ @Consumes("application/json")
+ public void register(@HeaderParam(value = "url") String url) throws Exception {
+ featuresGatewayService.register(url);
+ }
+
+ @DELETE
+ @Path("/")
+ @Consumes("application/json")
+ public void remove(@HeaderParam(value = "url") String url) throws Exception {
+ featuresGatewayService.remove(url);
+ }
+
+}
diff --git a/gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/rest/FeaturesGatewayRestService.java b/gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/rest/FeaturesGatewayRestService.java
new file mode 100644
index 0000000..b9db810
--- /dev/null
+++ b/gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/rest/FeaturesGatewayRestService.java
@@ -0,0 +1,68 @@
+/*
+ * 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.cave.gateway.service.rest;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.bus.extension.ExtensionManagerBus;
+import org.apache.cxf.transport.http.DestinationRegistry;
+import org.apache.cxf.transport.http.DestinationRegistryImpl;
+import org.apache.cxf.transport.http.HTTPTransportFactory;
+import org.apache.karaf.cave.gateway.FeaturesGatewayService;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.http.HttpService;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Component(name = "org.apache.karaf.cave.gateway.rest")
+public class FeaturesGatewayRestService {
+
+ @Reference
+ private FeaturesGatewayService featuresGatewayService;
+
+ @Reference
+ private HttpService httpService;
+
+ @Activate
+ public void activate(ComponentContext componentContext) throws Exception {
+ FeaturesGatewayRestApi restApi = new FeaturesGatewayRestApi(featuresGatewayService);
+
+ Map<Class<?>, Object> extensions = new HashMap<>();
+ DestinationRegistry destinationRegistry = new DestinationRegistryImpl();
+ HTTPTransportFactory httpTransportFactory = new HTTPTransportFactory(destinationRegistry);
+ extensions.put(HTTPTransportFactory.class, httpTransportFactory);
+ extensions.put(DestinationRegistry.class, destinationRegistry);
+ Bus bus = new ExtensionManagerBus(extensions, null, getClass().getClassLoader());
+ org.apache.cxf.transport.DestinationFactoryManager destinationFactoryManager = bus.getExtension(org.apache.cxf.transport.DestinationFactoryManager.class);
+ for (String url : HTTPTransportFactory.DEFAULT_NAMESPACES) {
+ destinationFactoryManager.registerDestinationFactory(url, httpTransportFactory);
+ }
+
+ FeaturesGatewayRestServlet featuresGatewayRestServlet = new FeaturesGatewayRestServlet(restApi, destinationRegistry, bus);
+ httpService.registerServlet("/cave/features-gateway/api", featuresGatewayRestServlet, null, null);
+ }
+
+ @Deactivate
+ public void deactivate() throws Exception {
+ httpService.unregister("/cave/features-gateway/api");
+ }
+
+}
diff --git a/rest/src/main/java/org/apache/karaf/cave/rest/CaveRestServlet.java b/gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/rest/FeaturesGatewayRestServlet.java
similarity index 51%
copy from rest/src/main/java/org/apache/karaf/cave/rest/CaveRestServlet.java
copy to gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/rest/FeaturesGatewayRestServlet.java
index 6801cfe..df973f7 100644
--- a/rest/src/main/java/org/apache/karaf/cave/rest/CaveRestServlet.java
+++ b/gateway/service/src/main/java/org/apache/karaf/cave/gateway/service/rest/FeaturesGatewayRestServlet.java
@@ -14,47 +14,38 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.rest;
+package org.apache.karaf.cave.gateway.service.rest;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
+import org.apache.cxf.Bus;
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
+import org.apache.cxf.transport.http.DestinationRegistry;
import org.apache.cxf.transport.servlet.CXFNonSpringServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
-public class CaveRestServlet extends CXFNonSpringServlet {
+public class FeaturesGatewayRestServlet extends CXFNonSpringServlet {
- private RepositoryRest repositoryRest;
- private DeployerRest deployerRest;
+ private FeaturesGatewayRestApi restApi;
- public CaveRestServlet(RepositoryRest repositoryRest, DeployerRest deployerRest) {
- this.repositoryRest = repositoryRest;
- this.deployerRest = deployerRest;
+ public FeaturesGatewayRestServlet(FeaturesGatewayRestApi restApi, DestinationRegistry destinationRegistry, Bus bus) {
+ super(destinationRegistry, false);
+ this.restApi = restApi;
+ this.setBus(bus);
}
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
-
- if (repositoryRest != null) {
- JAXRSServerFactoryBean repositoryBean = new JAXRSServerFactoryBean();
- repositoryBean.setAddress("/repository");
- repositoryBean.setBus(getBus());
- repositoryBean.setProvider(new JacksonJsonProvider());
- repositoryBean.setServiceBean(repositoryRest);
- repositoryBean.create();
- }
-
- if (deployerRest != null) {
- JAXRSServerFactoryBean deployerBean = new JAXRSServerFactoryBean();
- deployerBean.setAddress("/deployer");
- deployerBean.setBus(getBus());
- deployerBean.setProvider(new JacksonJsonProvider());
- deployerBean.setServiceBean(deployerRest);
- deployerBean.create();
+ if (restApi != null) {
+ JAXRSServerFactoryBean bean = new JAXRSServerFactoryBean();
+ bean.setAddress("/");
+ bean.setBus(getBus());
+ bean.setProvider(new JacksonJsonProvider());
+ bean.setServiceBean(restApi);
+ bean.create();
}
-
}
}
diff --git a/gateway/service/src/test/java/org/apache/karaf/cave/gateway/service/FeaturesGatewayServiceImplTest.java b/gateway/service/src/test/java/org/apache/karaf/cave/gateway/service/FeaturesGatewayServiceImplTest.java
new file mode 100644
index 0000000..3d1d2a0
--- /dev/null
+++ b/gateway/service/src/test/java/org/apache/karaf/cave/gateway/service/FeaturesGatewayServiceImplTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.cave.gateway.service;
+
+import org.easymock.EasyMock;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.service.http.HttpService;
+
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+public class FeaturesGatewayServiceImplTest {
+
+ private FeaturesGatewayServiceImpl featuresGatewayService;
+
+ @Before
+ public void setup() throws Exception {
+ HttpService httpService = EasyMock.createMock(HttpService.class);
+ featuresGatewayService = new FeaturesGatewayServiceImpl();
+ featuresGatewayService.setHttpService(httpService);
+ Dictionary<String, Object> properties = new Hashtable<>();
+ properties.put("storage.location", "target/cave/cave-features-gateway.xml");
+ featuresGatewayService.activate(properties);
+ }
+
+ @Test
+ public void testRegistrationUnregistration() throws Exception {
+ featuresGatewayService.register("mvn:org.apache.karaf.features/standard/4.2.6/xml/features");
+ Assert.assertEquals(1, featuresGatewayService.list().size());
+ Assert.assertEquals("mvn:org.apache.karaf.features/standard/4.2.6/xml/features", featuresGatewayService.list().get(0));
+ Assert.assertTrue(Files.exists(Paths.get("target/cave/cave-features-gateway.xml")));
+
+ featuresGatewayService.remove("mvn:org.apache.karaf.features/standard/4.2.6/xml/features");
+ Assert.assertEquals(0, featuresGatewayService.list().size());
+ }
+
+}
diff --git a/itest/pom.xml b/itest/pom.xml
new file mode 100644
index 0000000..5a84cbc
--- /dev/null
+++ b/itest/pom.xml
@@ -0,0 +1,133 @@
+<?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>cave</artifactId>
+ <version>4.2.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <groupId>org.apache.karaf.cave</groupId>
+ <artifactId>itest</artifactId>
+ <name>Apache Karaf :: Cave :: Integration Tests</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.karaf.itests</groupId>
+ <artifactId>common</artifactId>
+ <version>${karaf.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.karaf</groupId>
+ <artifactId>apache-karaf</artifactId>
+ <version>${karaf.version}</version>
+ <scope>test</scope>
+ <type>tar.gz</type>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.karaf</groupId>
+ <artifactId>org.apache.karaf.client</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-container-karaf</artifactId>
+ <version>4.13.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-junit4</artifactId>
+ <version>4.13.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-atinject_1.0_spec</artifactId>
+ <version>1.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.awaitility</groupId>
+ <artifactId>awaitility</artifactId>
+ <version>3.1.6</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.hamcrest</artifactId>
+ <version>1.3_1</version>
+ <scope>runtime</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.servicemix.tooling</groupId>
+ <artifactId>depends-maven-plugin</artifactId>
+ <version>1.4.0</version>
+ <executions>
+ <execution>
+ <id>generate-depends-file</id>
+ <goals>
+ <goal>generate-depends-file</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <forkCount>1</forkCount>
+ <reuseForks>false</reuseForks>
+ <systemPropertyVariables>
+ <org.ops4j.pax.logging.DefaultServiceLog.level>INFO</org.ops4j.pax.logging.DefaultServiceLog.level>
+ <spring31.version>3.1.4.RELEASE</spring31.version>
+ <spring32.version>3.2.18.RELEASE_1</spring32.version>
+ <spring40.version>4.0.9.RELEASE_1</spring40.version>
+ <spring41.version>4.1.9.RELEASE_1</spring41.version>
+ <spring42.version>4.2.9.RELEASE_1</spring42.version>
+ <spring43.version>4.3.25.RELEASE_1</spring43.version>
+ <spring50.version>5.0.15.RELEASE_1</spring50.version>
+ <spring51.version>5.1.9.RELEASE_1</spring51.version>
+ <spring.security31.version>3.1.4.RELEASE</spring.security31.version>
+ <spring.security42.version>4.2.4.RELEASE_1</spring.security42.version>
+ <spring.security51.version>5.1.5.RELEASE_1</spring.security51.version>
+ <activemq.version>5.15.9</activemq.version>
+ <cave.version>${project.version}</cave.version>
+ </systemPropertyVariables>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ No newline at end of file
diff --git a/itest/src/test/java/org/apache/karaf/cave/itest/repository/AllInTest.java b/itest/src/test/java/org/apache/karaf/cave/itest/repository/AllInTest.java
new file mode 100644
index 0000000..c045a5f
--- /dev/null
+++ b/itest/src/test/java/org/apache/karaf/cave/itest/repository/AllInTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.cave.itest.repository;
+
+import org.apache.karaf.itests.KarafTestSupport;
+import org.apache.karaf.jaas.boot.principal.RolePrincipal;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.karaf.options.KarafDistributionOption;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+
+import java.util.stream.Stream;
+
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class AllInTest extends KarafTestSupport {
+
+ @Configuration
+ public Option[] config() {
+ Option[] options = new Option[]{
+ KarafDistributionOption.editConfigurationFilePut("etc/system.properties", "cave.version", System.getProperty("cave.version"))
+ };
+ return Stream.of(super.config(), options).flatMap(Stream::of).toArray(Option[]::new);
+ }
+
+ @Test
+ public void test() throws Exception {
+ System.out.println(executeCommand("feature:repo-add cave " + System.getProperty("cave.version")));
+ System.out.println("==== Install cave-repository, cave-deployer, cave-features-gateway ====");
+ System.out.println(executeCommand("feature:install cave-repository", new RolePrincipal("admin")));
+ System.out.println(executeCommand("feature:install cave-deployer", new RolePrincipal("admin")));
+ System.out.println(executeCommand("feature:install cave-features-gateway", new RolePrincipal("admin")));
+
+ Thread.sleep(1000);
+
+ System.out.println("==== Installed Features ====");
+ String featureList = executeCommand("feature:list -i");
+ assertContains("cave-repository", featureList);
+ assertContains("cave-deployer", featureList);
+ assertContains("cave-features-gateway", featureList);
+ System.out.println(featureList);
+ String httpList = executeCommand("http:list");
+ System.out.println("==== HTTP List ====");
+ System.out.println(httpList);
+ assertContains("/cave/features-gateway/api", httpList);
+ assertContains("/cave/repository/api", httpList);
+ assertContains("/cave/deployer/api", httpList);
+ assertContainsNot("Failed", httpList);
+ }
+
+}
diff --git a/itest/src/test/java/org/apache/karaf/cave/itest/repository/DeployerTest.java b/itest/src/test/java/org/apache/karaf/cave/itest/repository/DeployerTest.java
new file mode 100644
index 0000000..511eb44
--- /dev/null
+++ b/itest/src/test/java/org/apache/karaf/cave/itest/repository/DeployerTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.cave.itest.repository;
+
+import org.apache.karaf.itests.KarafTestSupport;
+import org.apache.karaf.jaas.boot.principal.RolePrincipal;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.karaf.options.KarafDistributionOption;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+
+import java.util.stream.Stream;
+
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class DeployerTest extends KarafTestSupport {
+
+ @Configuration
+ public Option[] config() {
+ Option[] options = new Option[]{
+ KarafDistributionOption.editConfigurationFilePut("etc/system.properties", "cave.version", System.getProperty("cave.version"))
+ };
+ return Stream.of(super.config(), options).flatMap(Stream::of).toArray(Option[]::new);
+ }
+
+ @Test
+ public void test() throws Exception {
+ System.out.println(executeCommand("feature:repo-add cave " + System.getProperty("cave.version")));
+ System.out.println(executeCommand("feature:install cave-deployer", new RolePrincipal("admin")));
+ String featureList = executeCommand("feature:list -i");
+ int count = 0;
+ while (!featureList.contains("cave-deployer") && count < 100) {
+ featureList = executeCommand("feature:list -i");
+ Thread.sleep(100);
+ count++;
+ }
+ if (count >= 100) {
+ throw new RuntimeException("cave-deployer feature is not installed");
+ }
+ System.out.println("==== Installed Features ====");
+ System.out.println(featureList);
+ String httpList = executeCommand("http:list");
+ System.out.println("==== HTTP List ====");
+ System.out.println(httpList);
+ assertContains("/cave/deployer/api", httpList);
+
+ System.out.println("==== Create Connection ====");
+ executeCommand("cave:deployer-connection-register TEST " + getJmxServiceUrl() + " root karaf karaf");
+ System.out.println("==== Connections List ====");
+ String connectionList = executeCommand("cave:deployer-connection-list");
+ assertContains("TEST", connectionList);
+ System.out.println(connectionList);
+
+ System.out.println("==== List Features Via Deployer ====");
+ String featureListViaDeployer = executeCommand("cave:deployer-feature-list TEST");
+ assertContains("cave-deployer", featureListViaDeployer);
+ System.out.println(featureListViaDeployer);
+ }
+
+}
diff --git a/itest/src/test/java/org/apache/karaf/cave/itest/repository/FeaturesGatewayTest.java b/itest/src/test/java/org/apache/karaf/cave/itest/repository/FeaturesGatewayTest.java
new file mode 100644
index 0000000..7a1ba0e
--- /dev/null
+++ b/itest/src/test/java/org/apache/karaf/cave/itest/repository/FeaturesGatewayTest.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.cave.itest.repository;
+
+import org.apache.karaf.itests.KarafTestSupport;
+import org.apache.karaf.jaas.boot.principal.RolePrincipal;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.karaf.options.KarafDistributionOption;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+
+import java.util.stream.Stream;
+
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class FeaturesGatewayTest extends KarafTestSupport {
+
+ @Configuration
+ public Option[] config() {
+ Option[] options = new Option[]{
+ KarafDistributionOption.editConfigurationFilePut("etc/system.properties", "cave.version", System.getProperty("cave.version"))
+ };
+ return Stream.of(super.config(), options).flatMap(Stream::of).toArray(Option[]::new);
+ }
+
+ @Test
+ public void test() throws Exception {
+ System.out.println(executeCommand("feature:repo-add cave " + System.getProperty("cave.version")));
+ System.out.println(executeCommand("feature:install cave-features-gateway", new RolePrincipal("admin")));
+ String featureList = executeCommand("feature:list -i");
+ int count = 0;
+ while (!featureList.contains("cave-features-gateway") && count < 100) {
+ featureList = executeCommand("feature:list -i");
+ Thread.sleep(100);
+ count++;
+ }
+ if (count >= 100) {
+ throw new RuntimeException("cave-features-gateway feature is not installed");
+ }
+ System.out.println("==== Installed Features ====");
+ System.out.println(featureList);
+ String httpList = executeCommand("http:list");
+ System.out.println("==== HTTP List ====");
+ System.out.println(httpList);
+ assertContains("/cave/features-gateway/api", httpList);
+ }
+
+}
diff --git a/itest/src/test/java/org/apache/karaf/cave/itest/repository/RepositoryTest.java b/itest/src/test/java/org/apache/karaf/cave/itest/repository/RepositoryTest.java
new file mode 100644
index 0000000..03d4738
--- /dev/null
+++ b/itest/src/test/java/org/apache/karaf/cave/itest/repository/RepositoryTest.java
@@ -0,0 +1,155 @@
+/*
+ * 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.cave.itest.repository;
+
+import org.apache.karaf.itests.KarafTestSupport;
+import org.apache.karaf.jaas.boot.principal.RolePrincipal;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.karaf.options.KarafDistributionOption;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerMethod;
+
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.stream.Stream;
+
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerMethod.class)
+public class RepositoryTest extends KarafTestSupport {
+
+ @Configuration
+ public Option[] config() {
+ Option[] options = new Option[]{
+ KarafDistributionOption.editConfigurationFilePut("etc/system.properties", "cave.version", System.getProperty("cave.version"))
+ };
+ return Stream.of(super.config(), options).flatMap(Stream::of).toArray(Option[]::new);
+ }
+
+ @Test
+ public void installationTest() throws Exception {
+ System.out.println(executeCommand("feature:repo-add cave " + System.getProperty("cave.version")));
+ String repoList = executeCommand("feature:repo-list");
+ System.out.println("==== Features Repositories ====");
+ System.out.println(repoList);
+ assertContains("mvn:org.apache.karaf.cave/apache-karaf-cave/" + System.getProperty("cave.version") + "/xml/features", repoList);
+ System.out.println(executeCommand("feature:install cave-repository", new RolePrincipal("admin")));
+ String featureList = executeCommand("feature:list -i");
+ int count = 0;
+ while (!featureList.contains("cave-repository") && count < 100) {
+ featureList = executeCommand("feature:list -i");
+ Thread.sleep(100);
+ count++;
+ }
+ if (count >= 100) {
+ throw new RuntimeException("cave-repository feature is not installed");
+ }
+ System.out.println("==== Installed Features ====");
+ System.out.println(featureList);
+ String httpList = executeCommand("http:list");
+ System.out.println("==== HTTP List ====");
+ System.out.println(httpList);
+ assertContains("/cave/repository/api", httpList);
+ String repositoryService = executeCommand("service:list org.apache.karaf.cave.repository.RepositoryService");
+ assertContains("[org.apache.karaf.cave.repository.RepositoryService]", repositoryService);
+ assertContains("component.name = org.apache.karaf.cave.repository", repositoryService);
+ System.out.println("==== Cave Repository Service ====");
+ System.out.println(repositoryService);
+ }
+
+ @Test(timeout = 60000L)
+ public void repositoryTestViaCommands() throws Exception {
+ installCaveRepository();
+ System.out.println("==== Create TEST repository ====");
+ executeCommand("cave:repository-create TEST");
+ String repositoryList = executeCommand("cave:repository-list");
+ assertContains("TEST", repositoryList);
+ System.out.println("==== Repository List ====");
+ System.out.println(repositoryList);
+ System.out.println("==== HTTP List ====");
+ String httpList = executeCommand("http:list");
+ assertContains("TEST", httpList);
+ System.out.println(httpList);
+
+ System.out.println("==== Add Artifact in TEST ====");
+ executeCommand("cave:repository-artifact-add TEST mvn:commons-lang/commons-lang/2.6");
+
+ System.out.println("==== Check Artifact on TEST ====");
+ URL testUrl = new URL("http://localhost:" + getHttpPort() + "/cave/repository/TEST/commons-lang/commons-lang/2.6/commons-lang-2.6.jar");
+ HttpURLConnection connection = (HttpURLConnection) testUrl.openConnection();
+ Assert.assertEquals(200, connection.getResponseCode());
+ Assert.assertEquals("OK", connection.getResponseMessage());
+ Assert.assertEquals("application/octet-stream", connection.getContentType());
+
+ System.out.println("==== Delete Artifact from TEST ====");
+ executeCommand("cave:repository-artifact-delete TEST mvn:commons-lang/commons-lang/2.6");
+
+ System.out.println("==== Delete TEST repository ====");
+ executeCommand("cave:repository-remove TEST");
+ repositoryList = executeCommand("cave:repository-list");
+ assertContainsNot("TEST", repositoryList);
+
+ System.out.println("==== Create PROXY repository ====");
+ executeCommand("cave:repository-create -p http://repo1.maven.org/maven2\\@id=Central PROXY");
+
+ System.out.println("==== Repository List ====");
+ repositoryList = executeCommand("cave:repository-list");
+ assertContains("PROXY", repositoryList);
+ System.out.println(repositoryList);
+
+ System.out.println("==== Repository Info PROXY ====");
+ String repositoryInfo = executeCommand("cave:repository-info PROXY");
+ assertContains("Central", repositoryInfo);
+ System.out.println(repositoryInfo);
+
+ System.out.println("==== Try to get artifact on PROXY ====");
+ URL proxyUrl = new URL("http://localhost:" + getHttpPort() + "/cave/repository/PROXY/commons-lang/commons-lang/2.6/commons-lang-2.6.jar");
+ HttpURLConnection proxyUrlConnection = (HttpURLConnection) proxyUrl.openConnection();
+ Assert.assertEquals(200, proxyUrlConnection.getResponseCode());
+ Assert.assertEquals("OK", proxyUrlConnection.getResponseMessage());
+ Assert.assertEquals("application/octet-stream", proxyUrlConnection.getContentType());
+ Assert.assertEquals(284220, proxyUrlConnection.getContentLength());
+ System.out.println(executeCommand("cave:repository-remove PROXY"));
+
+ System.out.println("==== Create SCHEDULED repository ====");
+ executeCommand("cave:repository-create -s \"cron:0/5 * * * * ?\" -sa DELETE SCHEDULED");
+ repositoryInfo = executeCommand("cave:repository-info SCHEDULED");
+ assertContains("DELETE", repositoryInfo);
+ System.out.println(repositoryInfo);
+ System.out.println("==== Scheduler Jobs ====");
+ String scheduler = executeCommand("scheduler:list");
+ assertContains("cave-repository-SCHEDULED", scheduler);
+ System.out.println(scheduler);
+ System.out.println("==== Wait Scheduler ====");
+ repositoryList = executeCommand("cave:repository-list");
+ while (repositoryList.contains("SCHEDULED")) {
+ Thread.sleep(2000);
+ repositoryList = executeCommand("cave:repository-list");
+ }
+ System.out.println(repositoryList);
+ }
+
+ private void installCaveRepository() throws Exception {
+ executeCommand("feature:repo-add cave " + System.getProperty("cave.version"));
+ executeCommand("feature:install cave-repository", new RolePrincipal("admin"));
+ }
+
+}
diff --git a/itest/src/test/resources/etc/org.ops4j.pax.logging.cfg b/itest/src/test/resources/etc/org.ops4j.pax.logging.cfg
new file mode 100644
index 0000000..19bd51e
--- /dev/null
+++ b/itest/src/test/resources/etc/org.ops4j.pax.logging.cfg
@@ -0,0 +1,69 @@
+################################################################################
+#
+# 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.
+#
+################################################################################
+
+# Common pattern layout for appenders
+log4j2.pattern = %d{ISO8601} | %-5p | %-16t | %-32c{1} | %X{bundle.id} - %X{bundle.name} - %X{bundle.version} | %m%n
+
+# Root logger
+log4j2.rootLogger.level = INFO
+# uncomment to use asynchronous loggers, which require mvn:com.lmax/disruptor/3.3.2 library
+#log4j2.rootLogger.type = asyncRoot
+#log4j2.rootLogger.includeLocation = false
+log4j2.rootLogger.appenderRefs = RollingFile, PaxOsgi, Console
+log4j2.rootLogger.appenderRef.RollingFile.ref = RollingFile
+log4j2.rootLogger.appenderRef.PaxOsgi.ref = PaxOsgi
+log4j2.rootLogger.appenderRef.Console.ref = Console
+log4j2.rootLogger.appenderRef.Console.filter.threshold.type = ThresholdFilter
+log4j2.rootLogger.appenderRef.Console.filter.threshold.level = ${karaf.log.console:-OFF}
+
+# Appenders configuration
+log4j2.appenders = console, rolling, osgi
+
+# CONSOLE appender not used by default
+log4j2.appender.console.type = Console
+log4j2.appender.console.name = Console
+log4j2.appender.console.layout.type = PatternLayout
+log4j2.appender.console.layout.pattern = ${log4j2.pattern}
+
+# File appender
+log4j2.appender.rolling.type = RollingRandomAccessFile
+log4j2.appender.rolling.name = RollingFile
+log4j2.appender.rolling.fileName = ${karaf.log}/karaf.log
+log4j2.appender.rolling.filePattern = ${karaf.log}/karaf.log.%i
+# uncomment to not force a disk flush
+#log4j2.appender.rolling.immediateFlush = false
+log4j2.appender.rolling.append = true
+log4j2.appender.rolling.layout.type = PatternLayout
+log4j2.appender.rolling.layout.pattern = ${log4j2.pattern}
+log4j2.appender.rolling.policies.type = Policies
+log4j2.appender.rolling.policies.size.type = SizeBasedTriggeringPolicy
+log4j2.appender.rolling.policies.size.size = 16MB
+
+# OSGi appender
+log4j2.appender.osgi.type = PaxOsgi
+log4j2.appender.osgi.name = PaxOsgi
+log4j2.appender.osgi.filter = *
+
+# help with identification of maven-related problems with pax-url-aether
+#log4j2.logger.aether.name = shaded.org.eclipse.aether
+#log4j2.logger.aether.level = TRACE
+#log4j2.logger.http-headers.name = shaded.org.apache.http.headers
+#log4j2.logger.http-headers.level = DEBUG
+#log4j2.logger.maven.name = org.ops4j.pax.url.mvn
+#log4j2.logger.maven.level = TRACE
diff --git a/manual/pom.xml b/manual/pom.xml
index 1528f90..be82698 100644
--- a/manual/pom.xml
+++ b/manual/pom.xml
@@ -24,7 +24,7 @@
<parent>
<groupId>org.apache.karaf</groupId>
<artifactId>cave</artifactId>
- <version>4.1.3-SNAPSHOT</version>
+ <version>4.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
diff --git a/manual/src/main/asciidoc/index.adoc b/manual/src/main/asciidoc/index.adoc
index 9dccd31..3170ae5 100644
--- a/manual/src/main/asciidoc/index.adoc
+++ b/manual/src/main/asciidoc/index.adoc
@@ -12,14 +12,14 @@
// limitations under the License.
//
-Apache Karaf Cave 4.x - Documentation
+Apache Karaf Cave 4.2.x - Documentation
======================================
Apache Software Foundation
:doctype: article
:toc: left
:toclevels: 3
:toc-position: left
-:toc-title: Apache Karaf Cave 4.x - Documentation
+:toc-title: Apache Karaf Cave 4.2.x - Documentation
:numbered:
image::asf_logo.png[pdfwidth=35%,align=center]
@@ -28,20 +28,14 @@ include::overview.adoc[]
== User Guide
-include::user-guide/installation.adoc[]
+=== Repositories Manager
-include::user-guide/cave-repository.adoc[]
+include::user-guide/repository.adoc[]
-include::user-guide/populate-repository.adoc[]
+=== Karaf Features Gateway
-include::user-guide/proxy-repository.adoc[]
+include:user-guide/features-gateway.adoc[]
-include::user-guide/http-wrapper.adoc[]
-
-include::user-guide/maven-wrapper.adoc[]
-
-include::user-guide/administrate-cave.adoc[]
-
-include::user-guide/features-gateway.adoc[]
+=== Deployer
include::user-guide/deployer.adoc[]
diff --git a/manual/src/main/asciidoc/overview.adoc b/manual/src/main/asciidoc/overview.adoc
index aea2254..d68ab93 100644
--- a/manual/src/main/asciidoc/overview.adoc
+++ b/manual/src/main/asciidoc/overview.adoc
@@ -16,6 +16,16 @@
Apache Karaf Cave is an Apache Karaf sub-project.
+Apache Karaf Cave provides three different services:
+
+* Artifact repositories manager
+* Karaf features gateway
+* Deployer
+
+Each service is atomic and doesn't depend to another one. You can only use one service or combine the services.
+
+
+
Apache Karaf Cave is an implementation of an artifact repository and deployment platform for Apache Karaf supporting:
* OSGi Repository specification
diff --git a/manual/src/main/asciidoc/user-guide/administrate-cave.adoc b/manual/src/main/asciidoc/user-guide/administrate-cave.adoc
deleted file mode 100644
index 666ceb8..0000000
--- a/manual/src/main/asciidoc/user-guide/administrate-cave.adoc
+++ /dev/null
@@ -1,54 +0,0 @@
-//
-// 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.
-//
-
-=== Administration
-
-==== JMX
-
-When you install Apache Karaf Cave server, it provides a CaveServerMBean.
-
-This MBean uses the following object name:
-
-----
-org.apache.karaf.cave:type=repository,name=*
-----
-
-Thanks to this MBean, using any JMX client (like jconsole for instance), you can do all the same actions as you can using the `cave:*` commands:
-
-* void createRepository(String name, String location, boolean generate, boolean install) throws Exception;
-* void destroyRepository(String name) throws Exception;
-* void installRepository(String name) throws Exception;
-* void uninstallRepository(String name) throws Exception;
-* void populateRepository(String name, String url, boolean generate, String filter) throws Exception;
-* void proxyRepository(String name, String url, boolean generate, String filter) throws Exception;
-* void updateRepository(String name) throws Exception;
-* void uploadArtifact(String repository, String artifactUrl, boolean generate) throws Exception;
-
-==== REST
-
-Cave provides a complete REST API to manipulate the repositories.
-
-The API is available on:
-
-----
-http://localhost:8181/cave/rest
-----
-
-[NOTE]
-====
-8181 is the default port of the Apache Karaf HTTP service.
-====
-
-* `/cave/rest/repository` allows you to manipulate the Cave repositories.
-* `/cave/rest/deployer` allows you to manipulate the Cave deployer.
diff --git a/manual/src/main/asciidoc/user-guide/cave-repository.adoc b/manual/src/main/asciidoc/user-guide/cave-repository.adoc
deleted file mode 100644
index dbabdee..0000000
--- a/manual/src/main/asciidoc/user-guide/cave-repository.adoc
+++ /dev/null
@@ -1,103 +0,0 @@
-//
-// 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.
-//
-
-=== Repository
-
-A Cave repository is a container for:
-
-* Artifacts (files)
-* Repository metadata (optional, only used for OSGi repository)
-
-By default, a repository uses a filesystem backend for the storage, the directory used is KARAF_BASE/cave.
-
-You can change the storage location in the 'etc/org.apache.karaf.cave.server.storage.cfg' configuration file:
-
-----
-################################################################################
-#
-# 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.
-#
-################################################################################
-
-#
-# Storage location where Apache Karaf Cave create repositories by default
-#
-cave.storage.location=cave
-----
-
-For instance, you can define `/var/cave/store` for the `storage.location` property.
-
-==== Create
-
-The `cave:repository-create` command creates a new repository:
-
-----
-karaf@root()> cave:repository-create my-repository
-----
-
-A repository is identified by a name, `my-repository` in our example.
-
-Apache Karaf Cave creates the repository storage for you.
-
-If you want to use an existing directory, and avoid Cave creating one in the storage location, you can use the `-l`
-('--location') option:
-
-----
-karaf@root()> cave:repository-create -l /home/user/.m2/repository m2
-----
-
-By default, Apache Karaf Cave scans the repository storage and creates the repository metadata. You can use the `-no` (`--no-generate`)
-option to avoid this step:
-
-----
-karaf@root()> cave:repository-create -no -l /home/user/.m2/repository m2
-----
-
-By default, Apache Karaf Cave registers (installs) a new repository into the repository service. You can use the `-ns` (`--no-start`)
-option to avoid this step:
-
-----
-karaf@root()> cave:repository-create -ns -l /home/user/m2/repository m2
-----
-
-[NOTE]
-====
-The `-no` and `-ni` options are interesting when you use an existing location for the repository. If you create a
-new empty repository, these options don't have really any effect.
-====
-
-==== List
-
-You can list the repositories:
-
-----
-karaf@root()> cave:repositories
-Name | Location
-----------------------------------------------------------------
-my-repository | /opt/apache-karaf-4.0.0/data/cave/my-repository
-----
-
diff --git a/manual/src/main/asciidoc/user-guide/deployer.adoc b/manual/src/main/asciidoc/user-guide/deployer.adoc
index 7a783b6..855e448 100644
--- a/manual/src/main/asciidoc/user-guide/deployer.adoc
+++ b/manual/src/main/asciidoc/user-guide/deployer.adoc
@@ -12,115 +12,423 @@
// limitations under the License.
//
-=== Deployer
-
Cave Deployer allows you to manage a "farm" of Apache Karaf instances local or remote from the deployer itself.
-You have specific commands: `cave:deployer-*`.
-
==== Connections
-The first component of the deployer is the connection. A connection describes an access to an Apache Karaf instance.
+To interact with a Apache Karaf instance, you have to create a connection describing the access to this instance.
-You can create a connection via `cave:deployer-connection-register` command or the REST Deployer API.
-
-A connection contains:
+A connection has:
* an unique name for the connection
* the JMX URL to the given Apache Karaf instance
* the name of the Apache Karaf instance
* username and password to connect to the Apache Karaf instance
-For instance:
+===== Create connections
+
+====== `cave:deployer-connection-register` shell command
-----
+You can add a new Apache Karaf connection using `cave:deployer-connection-register` shell command:
+
+```
karaf@root()> cave:deployer-connection-register myconnection service:jmx:rmi:///jndi/rmi://localhost:1099/karaf-root root karaf karaf
-----
+```
+
+====== REST API
+
+You can create a connection via the Cave Deployer REST API on `/cave/deployer/api/connections` using a JSON description of your connection.
+For instance, using `curl`:
+
+```
+curl -X POST -H "Content-Type: application/json" http://localhost:8181/cave/deployer/api/connections -d '{
+ "name": "myconnection",
+ "jmxUrl": "service:jmx:rmi:///jndi/rmi://localhost:1099/karaf-root",
+ "karafName": "root",
+ "user": "karaf",
+ "password": "karaf"
+}'
+```
+
+====== JMX MBean
+
+The `org.apache.karaf.cave:type=deployer` MBean provides the `registerConnection(String name, String jmxUrl, String karafName, String user, String password)` operation
+allowing you to create a new connection.
+
+====== Service
+
+The `org.apache.karaf.cave.deployer.DeployerService` service provides the `registerConnection(Connection connection)` method allowing you to create a new connection.
+
+===== List connections
+
+====== `cave:deployer-connection-list` shell command
-You can list the connections using the `cave:deployer-connection` command:
+You can list available connections using the `cave:deployer-connection-list` command:
-----
-karaf@root()> cave:deployer-connection
+```
+karaf@root()> cave:deployer-connection-list
Name │ JMX URL │ Instance │ Username │ Password
─────────────┼─────────────────────────────────────────────────────────┼──────────┼──────────┼─────────
myconnection │ service:jmx:rmi:///jndi/rmi://localhost:1099/karaf-root │ root │ karaf │ *****
+```
-----
+====== REST API
-or via REST (with `curl` for instance):
+You can list available connections using the Cave Deployer REST API on `/cave/deployer/api/connections` (GET) URL.
-----
-curl -X GET -H "Content-Type: application/json" http://localhost:8181/cave/rest/deployer/connections
+For instance, using `curl`:
+
+```
+curl -X GET -H "Content-Type: application/json" http://localhost:8181/cave/deployer/api/connections
[{"name":"myconnection","jmxUrl":"service:jmx:rmi:///jndi/rmi://localhost:1099/karaf-root","karafName":"root","user":"karaf","password":"karaf"}]
-----
+```
+
+====== JMX MBean
+
+The `org.apache.karaf.cave:type=deployer` MBean provides the `connections` attribute. It's a tabular data where you can find all connections available.
+
+====== Service
+
+The `org.apache.karaf.cave.deployer.DeployerService` service provides the `connections()` method providing the list of all connections available.
+
+===== Delete a connection
+
+====== `cave:deployer-connection-delete` shell command
-You can remove a connection using `cave:deployer-connection-delete` command:
+You can delete an existing connection with `cave:deployer-connection-delete` shell command:
-----
+```
karaf@root()> cave:deployer-connection-delete myconnection
-----
+```
-or via REST:
+====== REST API
-----
-curl -X DELETE http://localhost:8181/cave/rest/deployer/connections/myconnection
-----
+You can delete an existing connection using Cave Deployer Rest API on `/cave/deployer/api/connections/{name}` URL (DELETE).
+For instance, using `curl`:
+
+```
+curl -X DELETE http://localhost:8181/cave/deployer/api/connections/myconnection
+```
+
+====== JMX MBean
+
+The `org.apache.karaf.cave:type=deployer` MBean provides the `deleteConnection(String name)` operation to delete an existing connection.
+
+====== Service
+
+The `org.apache.karaf.cave.deployer.DeployerService` service provides the `deleteConnection(String connectionName)` method to delete an existing connection.
==== Artifacts
-Cave Deployer to extract zip or kar files to a given Maven repository or local directory, thanks to the `cave:deployer-explode` and `cave:deployer-extract` commands
-and the `/cave/rest/deployer/artifact/explode` and `/cave/rest/deployer/artifact/extract` REST operations.
+===== Download & upload artifacts
+
+Cave Deployer is able to download an artifacts into a target directory.
+
+For instance you can download an artifact using `cave:deployer-download` shell command:
+
+```
+karaf@root()> cave:deployer-download mvn:commons-lang/commons-lang/2.6 /path/to/my/repo
+```
+
+You can also use Cave Deployer REST API on `/cave/deployer/api/download` (POST). The artifact URL is passed using `artifact` header and the directory is passed using `directory` header.
+
+For instance, using `curl`:
+
+```
+curl -X POST -H "artifact: mvn:commons-lang/commons-lang/2.6" -H "directory: /path/to/my/repo" http://localhost:8181/cave/deployer/api/download
+```
+
+You can also use the `download(String url, String directory)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+
+On the `org.apache.karaf.cave.deployer.DeployerService` service, you can also use the `download(String artifact, String directory)` method.
+
+On the other hand, you can also upload an artifact to a target repository providing the Maven coordinates. It's exactly the same as doing `mvn deploy:deploy-file`.
+
+To upload an artifact, you can use the `cave:deployer-upload` shell command:
+
+```
+karaf@root()> cave:deployer-upload -g groupId -a artifactId -v 1.0-SNAPSHOT mvn:foo/bar/x.x http://host/repository
+```
+
+You can also use the Cave Deployer REST API on `/cave/deployer/api/upload` (POST) with the following header parameters:
+
+* `groupId`
+* `artifactId`
+* `version`
+* `artifactUrl`
+* `repositoryUrl`
+
+For instance, using `curl`:
+
+```
+curl -X POST -H "groupId: groupId" -H "artifactId: artifactId" -H "version: 1.0-SNAPSHOT" -H "artifactUrl: mvn:foo/bar/x.x" -H "repositoryUrl: http://host/repository" http://localhost:8181/cave/deployer/api/upload
+```
+
+You can also use the `upload(String groupId, String artifactId, String version, String artifactUrl, String repositoryUrl)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+
+The `org.apache.karaf.cave.deployer.DeployerService` service also provides the `upload(String groupId, String artifactId, String version, String artifactUrl, String repositoryUrl)` method.
+
+===== Extract & explode artifacts
+
+Cave Deployer is also able to process zip and kar artifacts.
+
+Cave Deployer is able to extract zip or kar files to a given Maven repository or local directory:
+
+* explode will download and extract an artifact into a target folder/repository, looking for features repositories XML (typically in KAR files)
+* extract will download and extract an artifact into a target folder/repository
+
+To explode an artifact into a target Maven repository, you can use `cave:deployer-explode` shell command:
+
+```
+karaf@root()> cave:deployer-explode mvn:foo/bar/1.0/kar /path/to/repository
+```
+
+You can also use the Cave Deployer REST API on `/cave/deployer/api/explode` URL (POST) with `url` header for the artifact URL, and `repository` header for the repository URL. For instance, using `curl`:
+
+```
+curl -X POST -H "url: mvn:foo/bar/1.0/kar" -H "repository: /path/to/repository" http://localhost:8181/cave/deployer/api/explode
+["foo.xml"]
+```
+
+You can also use `explode(String url, String repository)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+
+The `org.apache.karaf.cave.deployer.DeployerService` also provides the `explode(String url, String repository)` method.
+
+Similar to explode, you can extract artifact (without looking for features repositories XML).
+
+You can use the `cave:deployer-extract` shell command to extract an artifact:
-You can also download an artifact from a Maven repository with the `cave:deployer-download` command (or `/cave/rest/deployer/artifact/download` REST operation)
-or even upload an artifact to a Maven repository thanks to the `cave:deployer-upload` command (or `/cave/rest/deployer/artifact/upload` REST operation).
+```
+karaf@root()> cave:deployer-extract mvn:foo/bar/1.0/zip /path/to/directory
+```
+
+You can also use the Cave Deployer REST API on `/cave/deployer/api/extract` URL (POST) with `url` header for the artifact URL, and `directory` header for the directory. For instance, using `curl`:
+
+```
+curl -X POST -H "url: mvn:foo/bar/1.0/zip" -H "directory: /path/to/directory" http://localhost:8181/cave/deployer/api/extract
+```
+
+The `org.apache.karaf.cave:type=deployer` MBean also provides `extract(String url, String directory)` operation to extract an artifact into a directory.
+
+You can also programmatically use ` extract(String url, String directory)` method on the `org.apache.karaf.cave.deployer.DeployerService` service.
==== Features
-Apache Karaf Cave Deployer provides the "classy" operations that you can do around Karaf features:
+===== Assemble features
+
+You can create new Karaf features by composing existing features, configurations or bundles.
+
+For instance, you can create `myfeature` composed by `feature1`, `feature2`, `bundle1`, `bundle2`.
+
+* `karaf@root()> cave:deployer-assemble-feature -g groupId -a artifactId -v 1.0-SNAPSHOT myfeature http://myrepo feature1 feature2`
+* Using `assembleFeature(String groupId, String artifactId, String version, String repositoryUrl, String feature, List<String> repositories, List<String> features, List<String> bundles)` operation on the `org.apache.karaf.cave:type=deployer` MBean
+* Using `assembleFeature(String groupId, String artifactId, String version, String repositoryUrl, String feature, List<String> featureRepositoryUrls, List<String> features, List<String> bundles, List<Config> configs)` method on the `org.apache.karaf.cave.deployer.DeployerService` service
+
+===== Add features repositories
+
+* `karaf@root()> cave:deployer-feature-repo-add myconnection mvn:foo/bar/1.0/xml/features`
+* `curl -X POST -H "artifactUrl: mvn:foo/bar/1.0/xml/features" http://localhost:8181/cave/deployer/api/connections/myconnection/features/repositories`
+* `addFeatureRepository(String url, String connection)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+* `addFeaturesRepository(String featuresRepositoryUrl, String connection)` method on the `org.apache.karaf.cave.deployer.DeployerService` service.
+
+===== List features repositories
+
+* `karaf@root()> cave:deployer-feature-repo-list myconnection`
+* `curl -X GET http://localhost:8181/cave/deployer/api/connections/myconnection/features/repositories`
+* `getFeatureRepositories(String connection)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+* `featuresRepositories(String connection)` method on the `org.apache.karaf.cave.deployer.DeployerService` service.
+
+===== List features provided by a features repository
+
+* `karaf@root()> cave:deployer-feature-repo-provide mvn:foo/bar/1.0/xml/features`
+* `curl -X GET -H "featuresRepositoryUrl: mvn:foo/bar/1.0/xml/features" http://localhost:8181/cave/deployer/api/features/repository`
+* `getProvidedFeatures(String featuresRepositoryUrl)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+* `providedFeatures(String featuresRepositoryUrl)` method on the `org.apache.karaf.cave.deployer.DeployerService` service.
+
+===== Remove features repositories
-* add a features repository
-* remove a features repository
-* list of features repositories
-* installing a feature
-* uninstalling a feature
-* list of features
+* `karaf@root()> cave:deployer-feature-repo-remove myconnection mvn:foo/bar/1.0/xml/features`
+* `curl -X DELETE -H "artifactUrl: mvn:foo/bar/1.0/xml/features" http://localhost:8181/cave/deployer/api/connections/myconnection/features/repositories`
+* `removeFeatureRepository(String repository, String connection)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+* `removeFeaturesRepository(String featuresRepositoryUrl, String connection)` method on the `org.apache.karaf.cave.deployer.DeployerService` service.
-You have dedicated Cave Deployer commands and REST operations for that.
+===== Install features
-You can also create a new "meta" feature by assembling existing features, configurations and bundles. This is done by the `assemble` operation:
+* `karaf@root()> cave:deployer-feature-install myconnection myfeature`
+* `curl -X POST http://localhost:8181/cave/deployer/api/connections/myconnection/features/myfeature`
+* `installFeature(String feature, String connection)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+* `installFeature(String feature, String connection)` method on the `org.apache.karaf.cave.deployer.DeployerService` service.
-* `cave:deployer-assemble-feature`
-* `/cave/rest/deployer/feature/assemble`
+===== List features
+
+* `karaf@root()> cave:deployer-feature-list myconnection`
+* `curl -X GET http://localhost:8181/cave/deployer/api/connections/myconnection/features`
+* `getFeatures(String connection)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+* `features(String connection)` method on the `org.apache.karaf.cave.deployer.DeployerService` service.
+
+===== List installed features
+
+* `karaf@root()> cave:deployer-feature-installed-list myconnection`
+* `installedFeatures(String connection)` method on the `org.apache.karaf.cave.deployer.DeployerService` service.
+
+===== Uninstall features
+
+* `karaf@root()> cave:deployer-feature-uninstall myconnection myfeature`
+* `curl -X DELETE http://localhost:8181/cave/deployer/api/connections/myconnection/features/myfeature`
+* `uninstallFeature(String feature, String connection)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+* `uninstallFeature(String feature, String connection)` method on the `org.apache.karaf.cave.deployer.DeployerService` service.
+
+==== KARs
+
+===== Install KAR
+
+* `karaf@root()> cave:deployer-kar-install myconnection mvn:foo/bar/1.0/kar`
+* `curl -X POST -H "artifactUrl: mvn:foo/bar/1.0/kar" http://localhost:8181/cave/deployer/api/connections/myconnection/kars`
+* `installKar(String url, String connection)` operation on the `org.apache.karaf.cave:typye=deployer` MBean.
+* `installKar(String karUrl, String connection)` on the `org.apache.karaf.cave.deployer.DeployerService` service.
+
+===== List KARs
+
+* `karaf@root()> cave:deployer-kar-list myconnection`
+* `curl -X GET http://localhost:8181/cave/deployer/api/connections/myconnection/kars`
+* `getKars(String connection)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+* `kars(String connection)` method on the `org.apache.karaf.cave.deployer.DeployerService` service.
+
+===== Uninstall KAR
+
+* `karaf@root()> cave:deployer-kar-uninstall myconnection mykar`
+* `curl -X DELETE http://localhost:8181/cave/deployer/api/connections/myconnection/kars/mykar`
+* `uninstallKar(String id, String connection)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+* `uninstallKar(String id, String connection)` method on the `org.apache.karaf.cave.deployer.DeployerService` service.
==== Bundles
-Apache Karaf Cave Deployer supports:
+===== Install bundle
+
+* `karaf@root()> cave:deployer-bundle-install myconnection mvn:foo/bar/1.0`
+* `curl -X POST -H "artifactUrl: mvn:foo/bar/1.0" http://localhost:8181/cave/deployer/api/connections/myconnection/bundles`
+* `installBundle(String url, String connection)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+* `installBundle(String artifactUrl, String connection)` method on the `org.apache.karaf.cave.deployer.DeployerService` service.
+
+===== List bundles
+
+* `karaf@root()> cave:deployer-bundle-list myconnection`
+* `curl -X GET http://localhost:8181/cave/deployer/api/connections/myconnection/bundles`
+* `getBundles(String connection)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+* `bundles(String connection)` method on the `org.apache.karaf.cave.deployer.DeployerService` service.
+
+===== Start bundle
+
+* `karaf@root()> cave:deployer-bundle-start myconnection 81`
+* `curl -X POST http://localhost:8181/cave/deployer/api/connections/myconnection/bundles/81/start`
+* `startBundle(String id, String connection)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+* `startBundle(String id, String connection)` method on the `org.apache.karaf.cave.deployer.DeployerService` service.
+
+===== Stop bundle
+
+* `karaf@root()> cave:deployer-bundle-stop myconnection 81`
+* `curl -X POST http://localhost:8181/cave/deployer/api/connections/myconnection/bundles/81/stop`
+* `stopBundle(String id, String connection)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+* `stopBundle(String id, String connection)` method on the `org.apache.karaf.cave.deployer.DeployerService` service.
+
+===== Uninstall bundle
+
+* `karaf@root()> cave:deployer-bundle-uninstall myconnection 81`
+* `curl -X DELETE http://localhost:8181/cave/deployer/api/connections/myconnection/bundles/81`
+* `uninstallBundle(String id, String connection)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+* `uninstallBundle(String id, String connection)` method on the `org.apache.karaf.cave.deployer.DeployerService` service.
+
+==== Configurations
+
+===== Create configuration
+
+* `karaf@root()> cave:deployer-config-create myconnection myconfig`
+* `curl -X POST http://localhost:8181/cave/deployer/api/connections/myconnection/configurations/myconfig`
+* `createConfig(String pid, String connection)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+* `createConfig(String pid, String connection)` method on the `org.apache.karaf.cave.deployer.DeployerService` service.
+
+===== Create configuration factory
+
+* `karaf@root()> cave:deployer-config-factory-create myconnection myfactory alias`
+* `curl -X POST http://localhost:8181/cave/deployer/api/connections/myconnection/configurations/factories/myfactory`
+* `createConfigFactory(String factoryPid, String alias, String connection)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+* `createConfigurationFactory(String factoryPid, String alias, String connection)` method on the `org.apache.karaf.cave.deployer.DeployerService` service.
+
+===== List configurations
+
+* `karaf@root()> cave:deployer-config-list myconnection`
+* `curl -X GET http://localhost:8181/cave/deployer/api/connections/myconnection/configurations`
+* `configs(String connection)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+* `configs(String connection)` method on the `org.apache.karaf.cave.deployer.DeployerService` service.
+
+===== Set configuration property
+
+* `karaf@root()> cave:deployer-config-property-set myconnection myconfiguration myproperty myvalue`
+* `setConfigProperty(String pid, String key, String value, String connection)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+* `setConfigProperty(String pid, String key, String value, String connection)` method on the `org.apache.karaf.cave.deployer.DeployerService` service.
+
+===== List configuration properties
+
+* `karaf@root()> cave:deployer-config-property-list myconnection myconfiguration`
+* `curl -X GET http://localhost:8181/cave/deployer/api/connections/myconnection/configurations/myconfiguration/properties`
+* `getConfigProperties(String pid, String connection)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+* `configProperties(String pid, String connection)` method on the `org.apache.karaf.cave.deployer.DeployerService` service.
+
+===== Delete configuration property
+
+* `karaf@root()> cave:deployer-config-property-delete myconnection myconfiguration myproperty`
+* `deleteConfigProperty(String pid, String key, String connection)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+* `deleteConfigProperty(String pid, String key, String connection)` methood on the `org.apache.karaf.cave.deployer.DeployerService` service.
+
+===== Delete configuration
+
+* `karaf@root()> cave:deployer-config-delete myconnection myconfiguration`
+* `curl -X DELETE http://localhost:8181/cave/deployer/api/connections/myconnection/configurations/myconfiguration`
+* `deleteConfig(String pid, String connection)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+* `deleteConfig(String pid, String connection)` method on the `org.apache.karaf.cave.deployer.DeployerService` service.
+
+==== Karaf Cellar cluster
+
+Cave Deployer is able to administrate a Karaf Cellar cluster.
+
+===== List cluster nodes
+
+* `cave:deployer-cluster-node-list myconnection`
+* `curl -X GET http://localhost:8181/cave/deployer/api/cluster/nodes`
+* `getClusterNodes(String connection)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+* `clusterNodes(String connection)` method on the `org.apache.karaf.cave.deployer.DeployerService` service.
+
+===== List cluster groups
-* install bundle
-* start bundle
-* stop bundle
-* uninstall bundle
-* list bundles
+* `cave:deployer-cluster-group-list myconnection`
+* `curl -X GET http://localhost:8181/cave/deployer/api/cluster/groups`
+* `getClusterGroups(String connection)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+* `clusterGroups(String connection)` method on the `org.apache.karaf.cave.deployer.DeployerService` service.
-==== KAR
+===== Add features repository to cluster group
-Apache Karaf Cave Deployer supports:
+* `cave:deployer-cluster-feature-repo-add myconnection myclustergroup mvn:foo/bar/1.0/xml/features`
+* `curl -X POST -H "url: mvn:foo/bar/1.0/xml/features" http://localhost:8181/cave/deployer/api/connections/myconnection/cluster/groups/myclustergroup/features/repositories`
+* `clusterFeatureRepositoryAdd(String url, String clusterGroup, String connection)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+* `clusterAddFeaturesRepository(String url, String clusterGroup, String connection)` method on the `org.apache.karaf.cave.deployer.DeployerService` service.
-* install KAR
-* uninstall KAR
-* list KARs
+===== Remove features repository from cluster group
-==== Config
+* `cave:cluster-feature-repo-remove myconnection myclustergroup mvn:foo/bar/1.0/xml/features`
+* `curl -X DELETE -H "url: mvn:foo/bar/1.0/xml/features" http://localhost:8181/cave/deployer/api/connections/myconnection/cluster/groups/myclustergroup/features/repositories`
+* `clusterFeatureRepositoryRemove(String url, String clusterGroup, String connection)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+* `clusterRemoveFeaturesRepository(String url, String clusterGroup, String Connection)` method on the `org.apache.karaf.cave.deployer.DeployerService` service.
-Apache Karaf Cave Deployer supports:
+===== Install feature on cluster group
-* creating a new configuration
-* update an existing configuration
-* add a new property to a configuration
-* delete a property from a configuration
-* delete a configuration
-* list of configurations
+* `cave:deployer-cluster-feature-install myconnection mygroup myfeature`
+* `curl -X POST http://localhost:8181/cave/deployer/api/connections/myconnection/cluster/groups/mygroup/features/myfeature`
+* `clusterFeatureInstall(String feature, String clusterGroup, String connection)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+* `clusterFeatureInstall(String feature, String clusterGroup, String connection)` method on the `org.apache.karaf.cave.deployer.DeployerService` service.
-==== Cellar
+===== Uninstall feature from cluster group
-Apache Karaf Cave Deployer supports features handling on a Cellar cluster.
+* `cave:deployer-cluster-feature-uninstall myconnection mygroup myfeature`
+* `curl -X DELETE http://localhost:8181/cave/deployer/api/connections/myconnection/cluster/groups/mygroup/features/myfeature`
+* `clusterFeatureUninstall(String feature, String clusterGroup, String connection)` operation on the `org.apache.karaf.cave:type=deployer` MBean.
+* `clusterFeatureUninstall(String feature, String clusterGroup, String connection)` method on the `org.apache.karaf.cave.deployer.DeployerService` service.
\ No newline at end of file
diff --git a/manual/src/main/asciidoc/user-guide/features-gateway.adoc b/manual/src/main/asciidoc/user-guide/features-gateway.adoc
index 91956e3..021fd62 100644
--- a/manual/src/main/asciidoc/user-guide/features-gateway.adoc
+++ b/manual/src/main/asciidoc/user-guide/features-gateway.adoc
@@ -12,73 +12,112 @@
// limitations under the License.
//
-=== Cave Features Gateway
-
-Apache Karaf Cave can also act as a central Karaf Features gateway. For instance, you can have an unique Cave server in your organization, providing
+Apache Karaf Cave can also act as a central Karaf Features gateway. For instance, you can have an unique Cave features gateway in your organization, providing
a single features repository for your Karaf instances.
Your Karaf instances on your ecosystem can connect to this unique Cave features gateway URL.
-Then, you manage your features repository on the "central" Cave server, and the Karaf instances get the features (without knowing exactly where they come from).
+Then, you manage your features repository on the "central" Cave Features Gateway, and the Karaf instances get the features (without knowing exactly where they come from).
-The Cave Features Gateway is automatically installed by the `cave-server` feature.
+By default, the gateway is expose on `/cave/features-gateway-repository`. It means that, on Karaf instances, you can do:
-==== Cave Features Gateway server
+```
+karaf@root()> feature:repo-add http://host:8181/cave/features-gateway-repository
+```
-You can manipulate the Features Gateway using specific shell commands or the corresponding MBean.
+NB: the Karaf instances use a cache, so you have to flush the changes on the gateway using the `feature:repo-refresh` command.
-===== Shell commands
+==== Installation
-You can register a features repository in the gateway using `cave:gateway-register` command. For example, you can register
-Apache Camel feature in the gateway using:
+Once Karaf Cave features repository is registered in a running Karaf instance (using `feature:repo-add cave` command for instance),
+you can install the Cave Features Gateway with the `cave-features-gateway` feature:
-----
-karaf@root()> cave:gateway-register mvn:org.apache.camel.karaf/apache-camel/2.18.1/xml/features
-----
+```
+karaf@root()> feature:repo-add cave
+karaf@root()> feature:install cave-features-gateway
+```
-Any URL (http, file, mvn, ...) supported by Apache Karaf is supported by Cave Features Gateway.
+==== Registering features repository in the gateway
-You can see the features repository registered in the gateway using the `cave:gateway-register` command:
+You can register several features repositories in the gateway. The main generated features repository for the gateway gathers the
+registered features repositories. Thanks to that the gateway is able to "expose" features from several repositories.
-----
-karaf@root()> cave:gateway-list
-mvn:org.apache.camel.karaf/apache-camel/2.18.1/xml/features
-----
+===== `cave:features-gateway-register` shell command
-You can also delete a features repository from the gateway using `cave:gateway-remove` command:
+You can simply register a features repository in the gateway using `cave:features-gateway-register` shell command. For instance,
+you can register Apache Camel Karaf features repository in the gateway:
-----
-karaf@root()> cave:gateway-remove mvn:org.apache.camel.karaf/apache-camel/2.18.1/xml/features
-----
+```
+karaf@root()> cave:features-gateway-register mvn:org.apache.camel.karaf/apache-camel/2.24.1/xml/features
+```
-===== MBean
+===== REST API
-Similar to the `cave:gateway*` commands, you find similar operation on the CaveGatewayMBean.
+You can also use the Cave Features Gateway REST API to register a features repository on `/cave/features-gateway/api`.
-This MBean uses the following object name:
+The repository URL is provided as `url` header.
-----
-org.apache.karaf.cave:type=gateway,name=*
-----
+For instance, using `curl`, you can do:
-This MBean provides the following operations:
+```
+curl -X POST -H "url: mvn:org.apache.camel.karaf/apache-camel/2.24.1/xml/features" http://localhost:8181/cave/features-gateway/api
+```
-* `List<String> list()`
-* `register(repository)`
-* `remove(repository)`
+===== JMX MBean
-==== Plugin your Karaf instances with the central gateway
+The `org.apache.karaf.cave:type=gateway` MBean provides the `register(String url)` operation allowing you to register a features repository in the gateway.
-The Cave Features Gateway is available on the Cave HTTP service at the following URL:
+===== Service
-----
-http://localhost:8181/cave/http/gateway
-----
+The `org.apache.karaf.cave.gateway.FeaturesGatewayService` service provides the `register(String url)` method allowing you to register a features repository in the gateway.
-Any Karaf instance can use the gateway using the regular `feature:repo-add` command:
+==== List registered features repositories in the gateway
-----
-karaf@root()> feature:repo-add http://cave_server:8181/cave/http/gateway
-----
+You can list the features repositories registered in the gateway.
-NB: the Karaf instances use a cache, so you have to flush the changes on the gateway using the `feature:repo-refresh` command.
+===== `cave:features-gateway-list` shell command
+
+The `cave:features-gateway-list` shell command gives you the registered features repositories in the gateway:
+
+```
+karaf@root()> cave:features-gateway-list
+mvn:org.apache.camel.karaf/apache-camel/2.24.1/xml/features
+```
+
+===== REST API
+
+You get list the features repositories registered on the Cave Features Gateway REST API, using `/cave/features-gateway/api` (GET). For instance using `curl`:
+
+```
+curl -X GET http://localhost:8181/cave/features-gateway/api
+["mvn:org.apache.camel.karaf/apache-camel/2.24.1/xml/features"]
+```
+
+===== JMX MBean
+
+The `repositories` attribute on the `org.apache.karaf.cave:type=gateway` MBean provides a tabular data with all registered features repositories.
+
+===== Service
+
+The `list()` method on the `org.apache.karaf.cave.gateway.FeaturesGatewayService` service provides the list of all registered features repositories.
+
+==== Remove a features repository from the gateway
+
+You can remove a features repository from the gateway.
+
+===== `cave:features-gateway-remove` shell command
+
+The `cave:features-gateway-remove` shell command allows you to remove a features repository from the gateway:
+
+```
+karaf@root()> cave:features-gateway-remove mvn:org.apache.camel.karaf/apache-camel/2.24.1/xml/features
+```
+
+===== REST API
+
+You can remove a registered features repository from the gateway using the REST API on `/cave/features-gateway/api` (DELETE). The features repository URL is passed via the `url` header.
+For instance, using `curl`:
+
+```
+curl -X DELETE -H "url: mvn:org.apache.camel.karaf/apache-camel/2.24.1/xml/features" http://localhost:8181/cave/features-gateway/api
+```
diff --git a/manual/src/main/asciidoc/user-guide/http-wrapper.adoc b/manual/src/main/asciidoc/user-guide/http-wrapper.adoc
deleted file mode 100644
index 9109e83..0000000
--- a/manual/src/main/asciidoc/user-guide/http-wrapper.adoc
+++ /dev/null
@@ -1,49 +0,0 @@
-//
-// 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.
-//
-
-=== HTTP wrapper service
-
-When you install the Apache Karaf Cave Server, it starts a HTTP service wrapper.
-
-It means that all artifacts and repository metadata present in local repositories are exposed over HTTP.
-
-==== Repository metadata access
-
-Assuming that you have the following repositories:
-
-----
-karaf@root()> cave:repositories
-Name | Location
------------------------------------------------------------------------------
-my-repository | /opt/apache-karaf-4.0.0/data/cave/my-repository
-----
-
-You can access the repository metadata using the following URL in your favorite browser:
-
-----
-http://localhost:8181/cave/http/my-repository-repository.xml
-----
-
-[NOTE]
-====
-The port 8181 is the default one of the Apache Karaf HTTP service.
-====
-
-You can see that the URL follows the format:
-
-----
-http://[cave_server_hostname]:[http_service_port]/cave/http/[cave_repository_name]-repository.xml
-----
-
-It means that you can register the repositories on remote Apache Karaf instances.
diff --git a/manual/src/main/asciidoc/user-guide/installation.adoc b/manual/src/main/asciidoc/user-guide/installation.adoc
deleted file mode 100644
index 0699d0f..0000000
--- a/manual/src/main/asciidoc/user-guide/installation.adoc
+++ /dev/null
@@ -1,55 +0,0 @@
-//
-// 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.
-//
-
-=== Installation
-
-This chapter describes how to install Apache Karaf Cave into an existing Apache Karaf instance.
-
-==== Pre-installation requirements
-
-As Apache Karaf Cave is an Apache Karaf sub-project, it has to be installed into a running Apache Karaf instance.
-
-Apache Karaf Cave is available as Apache Karaf features. The easiest way to install is just to have an internet
-connection from the running Apache Karaf instance.
-
-Apache Karaf Cave 4.0.x is designed to work on Apache Karaf 4.0.x.
-
-==== Registration of the Apache Karaf Cave features
-
-Simply register the Apache Karaf Cave features URL in your Apache Karaf instance:
-
-----
-karaf@root()> feature:repo-add cave 4.1.2
-Adding feature url mvn:org.apache.karaf.cave/apache-karaf-cave/4.1.2/xml/features
-----
-
-==== Starting Apache Karaf Cave
-
-The Apache Karaf Cave Server is installed by the 'cave-server' feature:
-
-----
-karaf@root()> feature:install cave
-----
-
-The `cave` feature installs all Cave features, including the repositories server and the deployer.
-
-After the installation of the cave-server feature, new commands are available:
-
-----
-karaf@root()> cave:<TAB>
-cave:repositories cave:repository-create cave:repository-destroy
-cave:repository-install cave:repository-populate cave:repository-proxy
-cave:repository-uninstall cave:repository-update cave:repository-upload
-...
-----
diff --git a/manual/src/main/asciidoc/user-guide/maven-wrapper.adoc b/manual/src/main/asciidoc/user-guide/maven-wrapper.adoc
deleted file mode 100644
index 3f5ff82..0000000
--- a/manual/src/main/asciidoc/user-guide/maven-wrapper.adoc
+++ /dev/null
@@ -1,54 +0,0 @@
-//
-// 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 wrapper service
-
-When you install the Apache Karaf Cave Server, it starts a Maven service wrapper.
-
-It means that all artifacts are available using a Maven structure (groupId/artifactId/version/artifactId-version[-classifier].type).
-
-It allows you to use your Cave repository directly using mvn URL in Karaf, and using Maven itself: Cave acts as a
-complete Maven repository.
-
-For instance, we have the following Cave repository:
-
-----
-karaf@root()> cave:repositories
-Name | Location
-----------------------------------------------------------------
-my-repository | /opt/apache-karaf-4.0.0/data/cave/my-repository
-----
-
-You can access the corresponding Maven repository using:
-
-----
-http://localhost:8181/cave/maven/repositories/my-repository
-----
-
-[NOTE]
-====
-The port 8181 is the default one of the Apache Karaf HTTP service.
-====
-
-You can see that the URL follows the format:
-
-----
-http://[cave_server_hostname]:[http_service_port]/cave/maven/repositories/my-repository
-----
-
-For instance, if a Cave repository contains the commons-lang 2.6 artifact, it's accessible using:
-
-----
-http://localhost:8181/cave/maven/repositories/my-repository/commons-lang/commons-lang/2.6/commons-lang-2.6.jar
-----
diff --git a/manual/src/main/asciidoc/user-guide/populate-repository.adoc b/manual/src/main/asciidoc/user-guide/populate-repository.adoc
deleted file mode 100644
index 2e36be9..0000000
--- a/manual/src/main/asciidoc/user-guide/populate-repository.adoc
+++ /dev/null
@@ -1,81 +0,0 @@
-//
-// 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.
-//
-
-=== Populate repository
-
-You can add new artifacts in a repository.
-
-==== Upload a single artifact
-
-You can upload a single artifact into a Cave Repository:
-
-----
-karaf@root()> cave:repository-upload my-repository file:/home/user/.m2/repository/org/apache/servicemix/bundles/org.apache.servicemix.bundles.asm/3.3_2/org.apache.servicemix.bundles.asm-3.3_2.jar
-karaf@root()> cave:repository-upload my-repository http://svn.apache.org/repos/asf/servicemix/m2-repo/org/apache/qpid/qpid-broker/0.8.0/qpid-broker-0.8.0.jar
-----
-
-You can also use a Maven style URL:
-
-----
-karaf@root()> cave:repository-upload my-repository mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.ant/1.7.0_5
-----
-
-==== Populate from an external repository
-
-You can also make a kind of "bulk" population of your repository, using an external repository:
-
-----
-karaf@root()> cave:repository-populate my-repository file:/home/user/.m2/repository
-----
-
-Apache Karaf Cave supports `file:` but also `http:` URLs. It means that Apache Karaf Cave is able to browse a remote repository and copy the artifacts
-to your "local" repository.
-
-For instance, you can populate your repository using all Ant ServiceMix bundles present on the Central Maven
-repository:
-
-----
-karaf@root()> cave:repository-populate my-repository http://repo1.maven.org/maven2/org/apache/servicemix/bundles/org.apache.servicemix.bundles.ant/
-----
-
-You can also populate with the whole Central Maven Repository:
-
-----
-karaf@root()> cave:repository-populate my-repository http://repo1.maven.org/maven2
-----
-
-Maven Central repository is really huge and populating from the whole Maven Central Repository will take
-a very very long time. It's just for demonstration purposes.
-
-You can filter the artifacts that you want to pick up to populate the repository. The `cave:repository-populate` command accepts
-a regex option for the filter. For instance, to pick up only joda-time version 2 artifact, you can run:
-
-----
-karaf@root()> cave:repository-populate --filter .*joda-time-2.* my-repository http://repo2.maven.org/maven2/joda-time/joda-time
-----
-
-When the remote repository requires HTTP authorization parameters, those can be specified in a Properties file:
-
-----
-karaf@root()> cave:repository-populate --properties <properties-file-path> my-repository <remote-repository-url>
-----
-
-where, `remote-repository-url` is the link to the external remote repository and `<properties-file-path>` is the path to a .properties file containing the following keys:
-
-----
-# Authorization parameters for remote repository
-cave.storage.http.username=
-cave.storage.http.password=
-----
-
diff --git a/manual/src/main/asciidoc/user-guide/proxy-repository.adoc b/manual/src/main/asciidoc/user-guide/proxy-repository.adoc
deleted file mode 100644
index 4f5f5b4..0000000
--- a/manual/src/main/asciidoc/user-guide/proxy-repository.adoc
+++ /dev/null
@@ -1,56 +0,0 @@
-//
-// 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.
-//
-
-=== Proxy repository
-
-As you can populate a repository, you can also proxy an "external" repository.
-
-It means that the artifacts stay on the remote repository, Apache Karaf Cave generates the repository metadata in the local repository
-for the remote artifacts:
-
-----
-karaf@root()> cave:repository-proxy my-repository http://repo1.maven.org/maven2/org/apache/servicemix/bundles/org.apache.servicemix.bundles.commons-lang/
-----
-
-[NOTE]
-====
-The Cave repository will only handle the repository metadata, it doesn't monitor the remote repository. It means that you
-have to call the `cave:proxy-repository` command each time the remote repository change (new artifacts, etc).
-====
-
-[NOTE]
-====
-A best practice is to create a Cave repository dedicated for each proxied repository.
-====
-
-The `cave:repository-proxy` command accepts the filter option, like the `cave:repository-populate` command:
-
-----
-karaf@root()> cave:repository-proxy --filter .*joda-time-2.* my-repository http://repo2.maven.org/maven2/joda-time/joda-time
-----
-
-The `cave:repository-proxy` command accepts the properties option, like the `cave:repository-populate` command:
-
-----
-karaf@root()> cave:repository-proxy --properties <properties-file-path> my-repository <remote-repository-url>
-----
-
-where, `remote-repository-url` is the link to the external remote repository and `<properties-file-path>` is the path to the .properties file containing the following keys:
-
-----
-# Authorization parameters for remote repository
-cave.storage.http.username=
-cave.storage.http.password=
-----
-
diff --git a/manual/src/main/asciidoc/user-guide/repository.adoc b/manual/src/main/asciidoc/user-guide/repository.adoc
new file mode 100644
index 0000000..dc0be67
--- /dev/null
+++ b/manual/src/main/asciidoc/user-guide/repository.adoc
@@ -0,0 +1,583 @@
+//
+// 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.
+//
+
+Cave Repositories Manager is a simple but complete artifacts repository manager. You can create repositories used
+to store artifact.
+The repositories are Maven and OSGi Bundle Repository compliant. A repository doesn't necessary store the artifacts, it
+can also serve as proxy or mirror to other remote repositories.
+
+==== Installation
+
+The first step is to register Cave features repository XML in a running Karaf instance:
+
+```
+karaf@root()> features:repo-add cave 4.2.0
+```
+
+Once it's done, you can install the `cave-repository` feature:
+
+```
+karaf@root()> feature:install cave-repository
+```
+
+The Cave Repository Manager service providing:
+
+* shell commands to manipulate the repositories
+* MBean to manipulate the repositories via JMX
+* REST API to manipulate the repositories with REST client (like `curl` for instance)
+
+==== Repositories
+
+A Cave repository is a artifact storage that clients can use (download or uploading artifacts).
+
+Repositories are managed by a `RepositoryService` and each repository has:
+
+* `name` is unique in the repository service.
+* `location` is where the artifacts are actually stored on the filesystem (it can be null if the repository is a proxy).
+* `url` is the HTTP location where the repository is accessible remotely.
+* `proxy` is a list of remote repositories that a repository can proxy.
+* `mirror` is a boolean used when the repository is a proxy. If `true`, when a client download an artifact from the repository, the artifact is download on remote repositories and cached on local repository storage. If `false`, the artifact is not cached on local repository storage.
+* `realm` is a JAAS security realm used to secure the users able to download/upload artifacts on the repository.
+* `download role` is the user role (on the realm) to secure artifacts download operation.
+* `scheduling` is the scheduler configuration (cron or date) to trigger an action on the repository. For instance `cron:0/20 * * * * ?` with execute action on the repository every 20 seconds.
+* `schedulingAction` is the action when scheduler is triggered. Possible actions are `PURGE`, `DELETE`, `COPY targetRepositoryName` and can be combined (comma separated). For instance: `PURGE,DELETE`.
+* `upload role` is the user role (on the realm) to secure artifacts upload operation.
+
+==== Creating repository and repository details
+
+===== `cave:repository-create` and `cave:repository-info` shell commands
+
+You can simply create a repository with the `cave:repository-create` command:
+
+```
+karaf@root()> cave:repository-create myrepo
+```
+
+By the default, `myrepo` repository has the following setup:
+
+* `myrepo` `location` is set to `${KARAF_DATA}/cave/repository/myrepo`. It can be changed at repository creation or later using `cave:repository-location` command (see later).
+* `myrepo` `url` is set to `/cave/repository/myrepo` (based on the Apache Karaf HTTP service). It can be changed at repository creation or later using `cave:repository-url` command (see later).
+* `myrepo` `proxy` is empty as, by default, repositories are "concrete" repository (with storage).
+* `myrepo` `realm` is `karaf` by default. It means that you can use roles defined in the `karaf` JAAS realm. You can take a look on the security and JAAS section of the Apache Karaf user guide for details.
+* `myrepo` `downloadRole` is empty, meaning that anyone can download artifacts from this repository.
+* `myrepo` `uploadRole` is empty, meaning that anyone can upload artifacts to this repository.
+* `myrepo` `scheduling` is empty, meaning no scheduler.
+* `myrepo` `schedulingAction` is empty, meaning no scheduler.
+
+The `cave:repository-create` command provides the options allowing you to specify repository settings:
+
+```
+DESCRIPTION
+ cave:repository-create
+
+ Create a new repository
+
+SYNTAX
+ cave:repository-create [options] name
+
+ARGUMENTS
+ name
+ The repository name
+ (required)
+
+OPTIONS
+ -m, --mirror
+ Enable repository mirror mode (for proxy)
+ -sa, --scheduling-action, --action, --actions
+ The repository scheduling action (DELETE, PURGE, COPY)
+ --help
+ Display this help message
+ -dr, --download-role
+ The repository security download role
+ -r, --realm
+ The repository security realm
+ (defaults to karaf)
+ -ur, --upload-role
+ The repository security upload role
+ -p, --proxy
+ The repository proxy locations
+ -l, --location
+ The repository location
+ -u, --url
+ The repository URL
+ -ps, --pool-size
+ The repository pool size for the HTTP service
+ (defaults to 8)
+ -s, --scheduling, --schedule
+ The repository scheduling (cron: or at:)
+```
+
+You can have details about existing repository using `cave:repository-info` command:
+
+```
+karaf@root()> cave:repository-info myrepo
+Name: myrepo
+Location: /opt/karaf/data/cave/repository/myrepo
+URL: /cave/repository/myrepo
+Proxy:
+Mirror: false
+Realm: karaf
+Download role:
+Upload role:
+Scheduling:
+Scheduling Actions:
+Pool size: 8
+```
+
+You can use "regular" Apache Karaf commands related to the services used by the repository.
+
+For instance, you can see the repository HTTP binding using `http:list` command:
+
+```
+karaf@root()> http:list
+ID │ Servlet │ Servlet-Name │ State │ Alias │ Url
+────┼───────────────────────┼────────────────────────────┼─────────────┼─────────────────────────┼─────────────────
+102 │ CXFNonSpringServlet │ cxf-osgi-transport-servlet │ Deployed │ /cxf │ [/cxf/*]
+116 │ MavenServlet │ ServletModel-2 │ Deployed │ /cave/repository/myrepo │ [/cave/repository/myrepo/*]
+116 │ RepositoryRestServlet │ ServletModel-4 │ Deployed │ /cave/repository/api │ [/cave/repository/api/*]
+```
+
+NB: `/cave/repository/api` URL is the default REST API repository service.
+
+It means, using your Internet browser, you can browse `myrepo` on `http://localhost:8181/cave/repository/myrepo/`.
+
+You can also see the realm and login modules with corresponding `jaas:realm-list` command.
+
+===== REST API
+
+It's also possible to create a repository using the REST API. You can find the WADL of the REST API on `/cave/repository/api?_wadl` URL.
+
+For instance, you can create a repository using `curl`:
+
+```
+curl -X POST -H "Content-Type: application/json" http://localhost:8181/cave/repository/api/repositories -d '{ "name":"myrepo", "location": "", "url": "", "proxy":"", "mirror": false,"realm":"karaf","downloadRole":"","uploadRole":"","poolSize":8}'
+```
+
+You can also have details about a repository using the REST API. For instance, using `curl`:
+
+```
+curl -X GET http://localhost:8181/cave/repository/api/repositories/myrepo
+{"name":"myrepo","location":"/opt/karaf/data/cave/repository/myrepo","url":"/cave/repository/myrepo","proxy":null,"mirror":false,"realm":"karaf","downloadRole":null,"uploadRole":null,"poolSize":8}
+```
+
+===== JMX MBean
+
+You can also create a repository using the `create(String name)` or `create(String name, String location, String url, String proxy, boolean mirror, String realm, String downloadRole, String uploadRole, int poolSize)` operations on the `org.apache.karaf.cave:type=repository` MBean.
+
+On the `Repositories` attribute on the `org.apache.karaf.cave:type=repository` MBean, you can get all details about any repository.
+
+===== Service
+
+You can also create a repository programmatically using `create(String name)` or `create(String name, String location, String url, String proxy, boolean mirror, String realm, String downloadRole, String uploadRole, int poolSize)` methods on the `org.apache.karaf.cave.repository.RepositoryService`.
+
+You just have to look for the `RepositoryService` in the Karaf service registry. For instance using DS programming model, you can do:
+
+```
+@Reference
+private org.apache.karaf.cave.repository.RepositoryService repositoryService;
+```
+
+==== Listing repositories
+
+===== `cave:repository-list` shell command
+
+You can list all repositories using the `cave:repository-list` shell command:
+
+```
+karaf@root()> cave:repository-list
+Name │ Location │ URL
+───────┼────────────────────────────────────────┼────────────────────────
+myrepo │ /opt/karaf/data/cave/repository/myrepo │ /cave/repository/myrepo
+```
+
+===== REST API
+
+You can get the list of all repositories via the Cave Repository REST API using GET method on `/cave/repository/api/repositories`. For instance using `curl`:
+
+```
+curl -X GET http://localhost:8181/cave/repository/api/repositories
+[{"name":"myrepo","location":"/home/jbonofre/Downloads/apache-karaf-4.2.7/data/cave/repository/myrepo","url":"/cave/repository/myrepo","proxy":null,"mirror":false,"realm":"karaf","downloadRole":null,"uploadRole":null,"poolSize":8}]
+```
+
+===== JMX MBean
+
+The `repositories` attribute on the `org.apache.karaf.cave:type=repository` MBean provides a tabular data with all repositories.
+
+===== Service
+
+On the `org.apache.karaf.cave.repository.RepositoryService`, you can programmatically get `org.apache.karaf.cave.repository.Repository` collection using `repositories()` method.
+
+==== Changing repository settings
+
+You can change repository settings after it has been created using dedicated operation.
+
+===== `cave:repository-url`, `cave:repository-proxy`, `cave:repository-location`, `cave:repository-security` shell commands
+
+You can see or change the repository location using `cave:repository-location` command:
+
+```
+karaf@root()> cave:repository-location myrepo
+/opt/karaf/data/cave/repository/myrepo
+karaf@root()> cave:repository-location myrepo /path/to/foo
+/path/to/foo
+```
+
+NB: The repository service will then move the repository location filesystem to the new location.
+
+You can see or change the repository binding HTTP URL using `cave:repository-url` command:
+
+```
+karaf@root()> cave:repository-url myrepo
+/cave/repository/myrepo
+karaf@root()> cave:repository-url myrepo /foo
+/foo
+```
+
+NB: The repository service will stop the repository HTTP service to start a new one on the new URL.
+
+You can see or change the repository proxy settings using `cave:repository-proxy` command:
+
+```
+karaf@root()> cave:repository-proxy myrepo
+null
+karaf@root()> cave:repository-proxy -m myrepo http://repo1.maven.org/maven2@id=central
+http://repo1.maven.org/maven2@id=central (mirror)
+```
+
+NB: When change the proxy settings, the repository location is not changed.
+
+You can see or change the repository security settings using `cave:repository-security` command:
+
+```
+karaf@root()> cave:repository-security myrepo
+Realm: karaf
+Download Role: null
+Upload Role: null
+karaf@root()> cave:repository-security -ur admin myrepo karaf
+Realm: karaf
+Download Role: null
+Upload Role: admin
+```
+
+NB: The repository service will restart the repository HTTP service with the new security settings.
+
+===== REST API
+
+You can use `/cave/repository/api/repositories/myrepo` URL with a updated repository json to update the repository settings. For instance using `curl`:
+
+```
+curl -X POST -H "Content-Type: application/json" http://localhost:8181/cave/repository/api/repositories/myrepo -d '{ "name":"myrepo", "location": "", "url": "", "proxy":"", "mirror": false,"realm":"karaf","downloadRole":"","uploadRole":"","poolSize":8}'
+```
+
+===== JMX MBean
+
+You have dedicated operations on `org.apache.karaf.cave:type=repository` MBean to change repository settings:
+
+* `changeLocation(String repositoryName, String newLocation)` to change the location of a repository. The repository service will actually move the location filesystem.
+* `changeUrl(String repositoryName, String new URL)` to change the URL of a repository. The repository service will stop the previous HTTP repository service to crerate start a new one.
+* `changeProxy(String repositoryName, String proxy, boolean mirror)` to change the proxy settings (URLs and mirror mode). The repository location is not changed.
+* `changeSecurity(String repositoryName, String realm, String downloadRole, String uploadRole)` to change the security settings (realm, download, and upload roles). The repository HTTP service will be restarted with the new security settings.
+
+===== Service
+
+On the `org.apache.karaf.cave.repository.RepositoryService`, you have the following methods to change repository settings:
+
+* `changeLocation(String repositoryName, String newLocation)` to change the location of a repository. The repository service will actually move the location filesystem.
+* `changeUrl(String repositoryName, String new URL)` to change the URL of a repository. The repository service will stop the previous HTTP repository service to crerate start a new one.
+* `changeProxy(String repositoryName, String proxy, boolean mirror)` to change the proxy settings (URLs and mirror mode). The repository location is not changed.
+* `changeSecurity(String repositoryName, String realm, String downloadRole, String uploadRole)` to change the security settings (realm, download, and upload roles). The repository HTTP service will be restarted with the new security settings.
+
+==== Upload artifacts
+
+You can upload artifacts in a repository using a regular HTTP client (via `curl` for instance).
+
+You can also directly use Maven (in your project using `distributionManagement` in the `pom.xml`) or Gradle.
+With Maven, you can also using `deploy:deploy-file` goal:
+
+```
+mvn deploy:deploy-file -Dfile=my.jar -Durl=http://localhost:8181/cave/repository/myrepo -DgroupId=foo -DartifactId=bar -Dversion=1.0-SNAPSHOT -Dpackaging=jar
+```
+
+===== `cave:repository-artifact-add` shell command
+
+
+For convenience, Cave Repository provides `cave:repository-artifact-add` command to upload artifact in a repository:
+
+```
+karaf@root()> cave:repository-artifact-add myrepo mvn:commons-lang/commons-lang/2.6
+```
+
+===== REST API
+
+You can upload an artifact using the Cave Repository REST API via `/cave/repository/api/repositories/myrepo/artifact` URL and providing the artifact URL as header:
+
+```
+curl -X POST -H "artifactUrl: mvn:commons-lang/commons-lang/2.6" http://localhost:8181/cave/repository/api/repositories/myrepo/artifact
+```
+
+===== JMX MBean
+
+The `org.apache.karaf.cave:type=repository` MBean provides the `addArtifact(String repositoryName, String artifactUrl)` operation allowing you to add an artifact in a repository.
+
+===== Service
+
+You can programmatically add artifact in a repository using `org.apache.karaf.cave.repository.RepositoryService` and the `addArtifact(String repositoryName, String artifactUrl)` method.
+
+==== Delete artifacts
+
+===== `cave:repository-artifact-delete` shell command
+
+The `cave:repository-artifact-delete` shell command allows you to delete an artifact from a repository:
+
+```
+karaf@root()> cave:repository-artifact-delete myrepo mvn:commons-lang/commons-lang/2.6
+```
+
+===== REST API
+
+You can delete an artifact from a repository using the Cave Repository REST API via `/cave/repository/api/repositories/myrepo/artifact` URL and providing the artifact URL as header:
+
+```
+curl -X DELETE -H "artifactUrl: mvn:commons-lang/commons-lang/2.6" http://localhost:8181/cave/repository/api/repositories/myrepo/artifact
+```
+
+===== JMX MBean
+
+The `org.apache.karaf.cave:type=repository` MBean provides the `deleteArtifact(String repositoryName, String artifactUrl)` operation allowing you to delete an artifact in a repository.
+
+===== Service
+
+You can programmatically delete artifact in a repository using `org.apache.karaf.cave.repository.RepositoryService` and the `addArtifact(String repositoryName, String artifactUrl)` method.
+
+==== Copy repositories content
+
+You can copy the whole content of a repository location into another repository location (it's a full recursive copy).
+
+===== `cave:repository-copy` shell command
+
+The `cave:repository-copy` shell command copies the whole content of a source repository to a destination repository:
+
+```
+karaf@root()> cave:repository-copy myrepo anotherrepo
+```
+
+===== REST API
+
+Copy is not allowed on the Cave Repository REST API.
+
+===== JMX MBean
+
+The `org.apache.karaf.cave:type=repository` MBean provides the `copy(String sourceRepository, String destinationRepository)` operation.
+
+===== Service
+
+The `org.apache.karaf.cave.repository.RepositoryService` service provides `copy(String sourceRepository, String destinationRepository)` method.
+
+==== Proxy and mirror
+
+A repository can proxy other repositories. The client (for instance Maven) request an artifact on the repository which "proxy" the request to other repositories define.
+
+If the mirror mode is enabled, the artifacts are copied into the repository storage location in addition of being delivered to the client (it's kind of artifacts caching).
+If the mirror mode is disabled, the repository storage is not used at all, and the artifact is passed directly.
+
+The proxy setting define the list of remote repositories (separated with coma). In addition, you can use `@id=` to give a name to the remote repository (just like in the `settings.xml`, but this is optional but recommended), `@snapshots` to indicate the remote repository can contains SNAPSHOT artifacts, `@noreleases` to indicate the remote repository doesn't only contains release artifacts.
+
+For instance, you can proxy Maven Central with the following command:
+
+```
+karaf@root()> cave:repository-create -p http://repo1.maven.org/maven2@id=central myrepo
+karaf@root()> cave:repository-info myrepo
+Name: myrepo
+Location: null
+URL: /cave/repository/myrepo
+Proxy: http://repo1.maven.org/maven2@id=central
+Mirror: false
+Realm: karaf
+Download role:
+Upload role:
+Pool size: 8
+```
+
+Now, let's request an artifact on `myrepo` repository using `http://localhost:8181/cave/repository/myrepo/commons-lang/commons-lang/2.6/commons-lang-2.6.jar` (remember `myrepo` is empty and doesn't have any location):
+
+```
+curl -O http://localhost:8181/cave/repository/myrepo/commons-lang/commons-lang/2.6/commons-lang-2.6.jar
+ % Total % Received % Xferd Average Speed Time Time Time Current
+ Dload Upload Total Spent Left Speed
+100 277k 100 277k 0 0 16.9M 0 --:--:-- --:--:-- --:--:-- 16.9M
+```
+
+We can see here, `myrepo` has correctly proxy the request to Maven Central.
+
+Now, let's add a location and enable `mirror` on `myrepo` repository:
+
+```
+karaf@root()> cave:repository-location myrepo /tmp
+karaf@root()> cave:repository-proxy -m myrepo http://repo1.apache.org/maven2@id=Central
+karaf@root()> cave:repository-info myrepo
+Name: myrepo
+Location: /tmp
+URL: /cave/repository/myrepo
+Proxy: http://repo1.apache.org/maven2@id=Central
+Mirror: true
+Realm: karaf
+Download role:
+Upload role:
+Scheduling:
+Scheduling Actions:
+Pool size: 8
+```
+
+And now, we perform the same request using `curl`:
+
+```
+curl -O http://localhost:8181/cave/repository/myrepo/commons-lang/commons-lang/2.6/commons-lang-2.6.jar
+ % Total % Received % Xferd Average Speed Time Time Time Current
+ Dload Upload Total Spent Left Speed
+100 277k 100 277k 0 0 16.9M 0 --:--:-- --:--:-- --:--:-- 16.9M
+```
+
+Now, if we check in the `myrepo` storage location (that we defined to `/tmp`), we can see the "cached" artifact:
+
+```
+ls -ltr /tmp/commons-lang/commons-lang/2.6/commons-lang-2.6.jar
+-rw-r--r-- 1 karaf karaf 284220 oct. 13 10:56 /tmp/commons-lang/commons-lang/2.6/commons-lang-2.6.jar
+```
+
+==== OSGi Bundle Repository
+
+Cave Repository supports OSGi Bundle Repository descriptor generation (aka `repository.xml`).
+
+It can scan all artifacts in a repository to generate the `repository.xml`.
+
+===== `cave:repository-update-bundle-descriptor` shell command
+
+The `cave:repository-update-bundle-descriptor` shell command trigger the scan of all artifacts in a repository to generate a `repository.xml`:
+
+```
+karaf@root()> cave:repository-update-bundle-descriptor myrepo
+```
+
+We now have a `repository.xml` generated (or updated) in the repository storage location. Of course, it's also available via HTTP on `http://localhost:8181/cave/repository/myrepo/repository.xml`.
+
+===== REST API
+
+You can trigger `repository.xml` update via the Cave Repository REST API on `/cave/repository/api/repositories/myrepo/bundle`. For instance using `curl`:
+
+```
+curl -X POST http://localhost:8181/cave/repository/api/repositories/myrepo/bundle
+```
+
+===== JMX MBean
+
+The `org.apache.karaf.cave:type=repository` MBean provides the `updateBundleRepositoryDescriptor(String repositoryName)` operation to update the OSGi Bundle Repository `repository.xml`.
+
+===== Service
+
+The `org.apache.karaf.cave.repository.RepositoryService` service provides the `updateBundleRepositoryDescriptor(String name)` method to update the OSGi Bundle Repository `repository.xml`.
+
+==== Purge
+
+You can completely purge a repository store, removing all artifacts.
+
+===== `cave:repository-purge` shell command
+
+You can cleanup completely a repository storage location using `cave:repository-purge` command:
+
+```
+karaf@root()> cave:repository-purge myrepo
+```
+
+NB: you will have an error if the repository doesn't have any storage location defined, for instance when the repository is only a proxy.
+
+===== REST API
+
+You can cleanup completely a repository storage using `/cave/repository/api/repositories/myrepo/purge` URL (POST). For instance, using `curl`:
+
+```
+curl X POST http://localhost:8181/cave/repository/api/repositories/myrepo/purge
+```
+
+===== JMX MBean
+
+The `org.apache.karaf.cave:type=repository` MBean provides the `purge(String repositoryName)` operation to trigger a repository location purge.
+
+===== Service
+
+The `org.apache.karaf.cave.repository.RepositoryService` service provides the `purge(String repositoryName)` method to trigger a repository location purge.
+
+==== Remove
+
+You can completely remove a repository from the Cave Repository Service (by default, the repository storage is not deleted). Optionally, you can also purge the storage location.
+
+===== `cave:repository-remove` shell command
+
+The `cave:repository-remove` shell command removes a repository, optionally (using `-p, --purge` option) removing the repository storage:
+
+```
+karaf@root()> cave:repository-remove -p myrepo
+```
+
+===== REST API
+
+You can delete a repository using `/cave/repository/api/repositories/myrepo` (DELETE). For instance, using `curl`:
+
+```
+curl -X DELETE http://localhost:8181/cave/repository/api/repositories/myrepo
+```
+
+NB: it's not possible to purge the repository storage when removing the repository via the REST API. You have first to purge the repository location before removing the repository.
+
+===== JMX MBean
+
+The `org.apache.karaf.cave;type=repository` MBean provides the `remove(String repositoryName, boolean purgeLocation)` operation to remove a repository. If `purgeLocation` is true, the repository storage will be cleanup, `false` else.
+
+===== Service
+
+The `org.apache.karaf.cave.repository.RepositoryService` service provides the `remove(String repositoryName, boolean purgeLocation)` method to remove a repository. If `purgeLocation` is true, the repository storage will be cleanup, `false` else.
+
+==== Repository Scheduler
+
+You can trigger action on a repository using Cave Scheduling feature.
+
+The repository scheduling supports cron (to periodically perform actions) or date (to execute actions at a specific time). For instance, you can perform actions every 5 seconds using `cron:0/5 * * * * ?` as repository scheduling.
+You can also execute actions at a certain date using `at:2020-05-13T13:56:45`.
+
+The valid repository scheduling actions are:
+
+* `PURGE` to cleanup the repository storage location.
+* `DELETE` to remove the repository.
+* `COPY targetRepositoryName` to copy all artifacts from the repository storage to another repository.
+
+The actions can be combined (comma separated). For instance, you can copy and purge using `COPY myrepo,PURGE` or purge and remove using `PURGE,DELETE`, etc.
+
+The repository scheduling and actions can be defined at repository creation time (using `-s` and `-sa` options on `cave:repository-create` shell command for instance), or later.
+
+===== `cave:repository-schedule` shell command
+
+The `cave:repository-schedule` shell command displays or set the current repository scheduling.
+
+For instance, you can set the scheduling using:
+
+```
+karaf@root()> cave:repository-schedule myrepo "cron:0/20 * * * * ?" "COPY destination"
+```
+
+===== JMX MBean
+
+The `org.apache.karaf.cave:type=repository` MBean provides the `changeScheduling(String name, String scheduling, String actions)` operation to change the repository scheduling.
+
+===== Service
+
+The `org.apache.karaf.cave.repository.RepositoryService` service provides the `changeScheduling(String name, String scheduling, String schedulingAction)` method to change the repository scheduling.
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index eeab177..18e7139 100644
--- a/pom.xml
+++ b/pom.xml
@@ -30,18 +30,19 @@
<groupId>org.apache.karaf</groupId>
<artifactId>cave</artifactId>
- <version>4.1.3-SNAPSHOT</version>
+ <version>4.2.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Apache Karaf :: Cave</name>
<properties>
<commons-io.version>2.6</commons-io.version>
- <cxf.version>3.1.18</cxf.version>
- <httpclient.version>4.3.4</httpclient.version>
- <jsoup.version>1.7.3</jsoup.version>
- <karaf.version>4.1.7</karaf.version>
+ <cxf.version>3.3.2</cxf.version>
+ <httpclient.version>4.5.10</httpclient.version>
+ <jackson.version>2.10.0</jackson.version>
+ <jsoup.version>1.12.1</jsoup.version>
+ <karaf.version>4.2.7</karaf.version>
<osgi.version>6.0.0</osgi.version>
- <commons-codec.version>1.10</commons-codec.version>
+ <commons-codec.version>1.13</commons-codec.version>
<wagon.version>1.0</wagon.version>
<servlet.spec.groupId>javax.servlet</servlet.spec.groupId>
@@ -52,63 +53,55 @@
</properties>
<modules>
- <module>server</module>
+ <module>repository</module>
+ <module>gateway</module>
<module>deployer</module>
- <module>rest</module>
<module>manual</module>
<module>assembly</module>
+ <module>itest</module>
</modules>
<scm>
<connection>scm:git:https://gitbox.apache.org/repos/asf/karaf-cave.git</connection>
<developerConnection>scm:git:https://gitbox.apache.org/repos/asf/karaf-cave.git</developerConnection>
<url>https://gitbox.apache.org/repos/asf?p=karaf-cave.git;a=summary</url>
- <tag>HEAD</tag>
</scm>
<dependencyManagement>
<dependencies>
<dependency>
- <groupId>org.apache.karaf.cave.server</groupId>
- <artifactId>org.apache.karaf.cave.server.api</artifactId>
+ <groupId>org.apache.karaf.cave.repository</groupId>
+ <artifactId>org.apache.karaf.cave.repository.api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
- <groupId>org.apache.karaf.cave.server</groupId>
- <artifactId>org.apache.karaf.cave.server.storage</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.apache.karaf.cave.server</groupId>
- <artifactId>org.apache.karaf.cave.server.command</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.apache.karaf.cave.server</groupId>
- <artifactId>org.apache.karaf.cave.server.management</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.apache.karaf.cave.server</groupId>
- <artifactId>org.apache.karaf.cave.server.maven</artifactId>
+ <groupId>org.apache.karaf.cave.repository</groupId>
+ <artifactId>org.apache.karaf.cave.repository.service</artifactId>
<version>${project.version}</version>
</dependency>
+
<dependency>
- <groupId>org.apache.karaf.cave.server</groupId>
- <artifactId>org.apache.karaf.cave.server.rest</artifactId>
+ <groupId>org.apache.karaf.cave.gateway</groupId>
+ <artifactId>org.apache.karaf.cave.gateway.api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
- <groupId>org.apache.karaf.cave.server</groupId>
- <artifactId>org.apache.karaf.cave.server.http</artifactId>
+ <groupId>org.apache.karaf.cave.gateway</groupId>
+ <artifactId>org.apache.karaf.cave.gateway.service</artifactId>
<version>${project.version}</version>
</dependency>
+
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
+ <groupId>com.fasterxml.jackson.jaxrs</groupId>
+ <artifactId>jackson-jaxrs-json-provider</artifactId>
+ <version>${jackson.version}</version>
+ </dependency>
+ <dependency>
<groupId>org.apache.karaf.shell</groupId>
<artifactId>org.apache.karaf.shell.core</artifactId>
<version>${karaf.version}</version>
@@ -119,6 +112,11 @@
<version>${karaf.version}</version>
</dependency>
<dependency>
+ <groupId>org.apache.karaf.scheduler</groupId>
+ <artifactId>org.apache.karaf.scheduler.core</artifactId>
+ <version>${karaf.version}</version>
+ </dependency>
+ <dependency>
<groupId>org.apache.karaf</groupId>
<artifactId>org.apache.karaf.util</artifactId>
<version>${karaf.version}</version>
@@ -178,7 +176,13 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
- <version>4.11</version>
+ <version>4.12</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <version>4.0.2</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -222,7 +226,7 @@
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
- <version>4.1.0</version>
+ <version>4.2.1</version>
<extensions>true</extensions>
<configuration>
<instructions>
@@ -244,8 +248,8 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
- <source>1.7</source>
- <target>1.7</target>
+ <source>1.8</source>
+ <target>1.8</target>
</configuration>
</plugin>
<plugin>
@@ -425,7 +429,7 @@
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
<configuration>
- <source>1.7</source>
+ <source>1.8</source>
</configuration>
<executions>
<execution>
diff --git a/server/command/pom.xml b/repository/api/pom.xml
similarity index 64%
rename from server/command/pom.xml
rename to repository/api/pom.xml
index 79cb1a3..90094e5 100644
--- a/server/command/pom.xml
+++ b/repository/api/pom.xml
@@ -23,43 +23,32 @@
<parent>
<groupId>org.apache.karaf.cave</groupId>
- <artifactId>org.apache.karaf.cave.server</artifactId>
- <version>4.1.3-SNAPSHOT</version>
+ <artifactId>org.apache.karaf.cave.repository</artifactId>
+ <version>4.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
- <groupId>org.apache.karaf.cave.server</groupId>
- <artifactId>org.apache.karaf.cave.server.command</artifactId>
- <name>Apache Karaf :: Cave :: Server :: Command</name>
+ <groupId>org.apache.karaf.cave.repository</groupId>
+ <artifactId>org.apache.karaf.cave.repository.api</artifactId>
+ <name>Apache Karaf :: Cave :: Repository :: API</name>
<packaging>bundle</packaging>
- <dependencies>
- <dependency>
- <groupId>org.apache.karaf.shell</groupId>
- <artifactId>org.apache.karaf.shell.core</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.karaf.cave.server</groupId>
- <artifactId>org.apache.karaf.cave.server.api</artifactId>
- </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>
+ <inherited>true</inherited>
+ <extensions>true</extensions>
<configuration>
<instructions>
- <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Export-Package>
+ org.apache.karaf.cave.repository
+ </Export-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
-</project>
+</project>
\ No newline at end of file
diff --git a/repository/api/src/main/java/org/apache/karaf/cave/repository/Repository.java b/repository/api/src/main/java/org/apache/karaf/cave/repository/Repository.java
new file mode 100644
index 0000000..e479438
--- /dev/null
+++ b/repository/api/src/main/java/org/apache/karaf/cave/repository/Repository.java
@@ -0,0 +1,238 @@
+/*
+ * 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.cave.repository;
+
+/**
+ * Cave repository.
+ */
+public class Repository {
+
+ private String name;
+ private String location;
+ private String url;
+ private String proxy;
+ private boolean mirror;
+ private String realm;
+ private String downloadRole;
+ private String uploadRole;
+ private String scheduling;
+ private String schedulingAction;
+ private int poolSize;
+
+ /**
+ * Get repository name.
+ *
+ * @return the repository name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the repository name.
+ *
+ * @param name the repository name.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get the repository storage location.
+ *
+ * @return the repository storage location.
+ */
+ public String getLocation() {
+ return location;
+ }
+
+ /**
+ * Set the repository storage location.
+ *
+ * @param location the repository storage location.
+ */
+ public void setLocation(String location) {
+ this.location = location;
+ }
+
+ /**
+ * Get the HTTP URL of the repository.
+ *
+ * @return The repository HTTP URL.
+ */
+ public String getUrl() {
+ return url;
+ }
+
+ /**
+ * Set the HTTP URL of the repository.
+ *
+ * @param url The repository HTTP URL.
+ */
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ /**
+ * Get the proxied repositories by this one.
+ *
+ * @return The repositories proxied by this repository.
+ */
+ public String getProxy() {
+ return proxy;
+ }
+
+ /**
+ * Set the proxied repositories by this one.
+ *
+ * @param proxy The repositories proxied by this repository.
+ */
+ public void setProxy(String proxy) {
+ this.proxy = proxy;
+ }
+
+ /**
+ * Get the proxy mode (mirroring or not).
+ *
+ * @return The repositories proxy mode, true for mirroring, false else.
+ */
+ public boolean isMirror() {
+ return mirror;
+ }
+
+ /**
+ * Set the proxy mode (mirroring or not).
+ *
+ * @param mirror true to set the proxy mode to mirror, false else.
+ */
+ public void setMirror(boolean mirror) {
+ this.mirror = mirror;
+ }
+
+ /**
+ * Get the JAAS realm used to secure the repository access.
+ *
+ * @return the name of JAAS realm.
+ */
+ public String getRealm() {
+ return realm;
+ }
+
+ /**
+ * Set the JAAS realm name used to secure the repository access.
+ *
+ * @param realm the name of the JAAS realm to use with this repository.
+ */
+ public void setRealm(String realm) {
+ this.realm = realm;
+ }
+
+ /**
+ * Get the user role allowed to download artifacts on this repository.
+ *
+ * @return the user role name.
+ */
+ public String getDownloadRole() {
+ return downloadRole;
+ }
+
+ /**
+ * Set the user role name allowed to download artifacts on this repository.
+ *
+ * @param downloadRole the user role name.
+ */
+ public void setDownloadRole(String downloadRole) {
+ this.downloadRole = downloadRole;
+ }
+
+ /**
+ * Get the user role allowed to upload artifacts on this repository.
+ *
+ * @return the user role name.
+ */
+ public String getUploadRole() {
+ return uploadRole;
+ }
+
+ /**
+ * Set the user role name allowed to upload artifacts on this repository.
+ *
+ * @param uploadRole the user role name.
+ */
+ public void setUploadRole(String uploadRole) {
+ this.uploadRole = uploadRole;
+ }
+
+ /**
+ * Get the scheduling of the repository. Valid format is cron:xx, at:xx or simply cron.
+ * For instance:
+ * cron:0 0/10 * * * ?
+ * at:2014-05-13T13:56:45
+ * @return the current repository scheduling or {@code null} if not defined.
+ */
+ public String getScheduling() {
+ return scheduling;
+ }
+
+ /**
+ * Set the repository scheduling. Valid format is cron:xx, at:xx or simply cron.
+ * For instance:
+ * cron:0 0/10 * * * ?
+ * at:2014-05-13T13:56:45
+ *
+ * @param scheduling the new repository scheduling.
+ */
+ public void setScheduling(String scheduling) {
+ this.scheduling = scheduling;
+ }
+
+ /**
+ * Get the action performed at scheduling. Valid values are DELETE, PURGE, COPY (it can be combined using comma as separator).
+ *
+ * @return the action performed at scheduling (DELETE, PURGE, COPY).
+ */
+ public String getSchedulingAction() {
+ return schedulingAction;
+ }
+
+ /**
+ * Set the action performed at scheduling. Valid values are DELETE, PURGE, COPY (it can combined using comma as separator).
+ *
+ * @param schedulingAction the action performed at scheduling (DELETE, PURGE, COPY).
+ */
+ public void setSchedulingAction(String schedulingAction) {
+ this.schedulingAction = schedulingAction;
+ }
+
+ /**
+ * Get the pool size for the repository Maven servlet.
+ *
+ * @return the pool size.
+ */
+ public int getPoolSize() {
+ return poolSize;
+ }
+
+ /**
+ * Set the pool size for the repository Maven servlet.
+ *
+ * @param poolSize the pool size.
+ */
+ public void setPoolSize(int poolSize) {
+ this.poolSize = poolSize;
+ }
+}
diff --git a/repository/api/src/main/java/org/apache/karaf/cave/repository/RepositoryService.java b/repository/api/src/main/java/org/apache/karaf/cave/repository/RepositoryService.java
new file mode 100644
index 0000000..2b4e980
--- /dev/null
+++ b/repository/api/src/main/java/org/apache/karaf/cave/repository/RepositoryService.java
@@ -0,0 +1,220 @@
+/*
+ * 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.cave.repository;
+
+import java.util.Collection;
+
+/**
+ * Manage Cave repositories.
+ */
+public interface RepositoryService {
+
+ /**
+ * Create a repository with a given name.
+ *
+ * @param name the repository name.
+ * @return the {@link Repository} created.
+ */
+ Repository create(String name) throws Exception;
+
+ /**
+ * Create a repository with a given name and a storage location.
+ *
+ * @param name the repository name.
+ * @param location the repository storage location.
+ * @return the {@link Repository} created.
+ */
+ Repository create(String name, String location) throws Exception;
+
+ /**
+ * Create a repositoty with a given name, storage location, and proxy URL.
+ *
+ * @param name the repository name.
+ * @param location the repository storage location.
+ * @param proxy the repository proxy URL.
+ * @return the {@link Repository} created.
+ */
+ Repository create(String name, String location, String proxy) throws Exception;
+
+ /**
+ * Create a repository with a given name, storage location, proxy and mirror repositories.
+ *
+ * @param name the repository name.
+ * @param location the repository storage location.
+ * @param proxy the repository proxy URL.
+ * @param mirror the repository proxy mode (true for mirroring, false else).
+ * @return the {@link Repository} created.
+ */
+ Repository create(String name, String location, String proxy, boolean mirror) throws Exception;
+
+ /**
+ * Create a repository with a given name, a storage location and all security settings (realm and user roles).
+ *
+ * @param name the repository name.
+ * @param location the repository storage location.
+ * @param url the repository HTTP URL.
+ * @param proxy the repositories proxied by this repository.
+ * @param mirror the repository proxy mode (true for mirroring, false else).
+ * @param realm the JAAS realm name.
+ * @param downloadRole the user role name allowed to download artifacts on this repository.
+ * @param uploadRole the user role name allowed to upload artifacts on this repository.
+ * @param scheduling the scheduling on this repository.
+ * @param schedulingAction the action performed at scheduling time.
+ * @param poolSize the pool size used internally by the repository Maven servlet.
+ * @return the {@link Repository} created.
+ */
+ Repository create(String name, String location, String url, String proxy, boolean mirror, String realm, String downloadRole, String uploadRole, String scheduling, String schedulingAction, int poolSize) throws Exception;
+
+ /**
+ * Remove a repository.
+ * NB: by default, the repository storage is not cleanup.
+ *
+ * @param name the repository name.
+ */
+ void remove(String name) throws Exception;
+
+ /**
+ * Remove a repository, eventually cleaning the repository storage.
+ *
+ * @param name the repository name.
+ * @param storageCleanup true to cleanup the repository storage, false else.
+ */
+ void remove(String name, boolean storageCleanup) throws Exception;
+
+ /**
+ * Purge the storage of an existing repository.
+ *
+ * @param name the repository name.
+ */
+ void purge(String name) throws Exception;
+
+ /**
+ * Change the location of an existing repository.
+ *
+ * @param name the repository name.
+ * @param location the new repository location.
+ */
+ void changeLocation(String name, String location) throws Exception;
+
+ /**
+ * Change the URL of an existing repository.
+ *
+ * @param name the repository name.
+ * @param url the new repository URL.
+ */
+ void changeUrl(String name, String url) throws Exception;
+
+ /**
+ * Change the proxy URL of an existing repository.
+ *
+ * @param name the repository name.
+ * @param proxy the new repository proxy URL.
+ * @param mirror the new repository proxy mode (true for mirroring, false else).
+ */
+ void changeProxy(String name, String proxy, boolean mirror) throws Exception;
+
+ /**
+ * Change the repository security configuration.
+ *
+ * @param name the repository name.
+ * @param realm the JAAS realm to use with.
+ * @param downloadRole the JAAS role used for download.
+ * @param uploadRole the JAAS role used for upload.
+ */
+ void changeSecurity(String name, String realm, String downloadRole, String uploadRole) throws Exception;
+
+ /**
+ * Change the repository scheduling configuration.
+ *
+ * @param name the repository name.
+ * @param scheduling the scheduling (cron or trigger) for this repository.
+ * @param schedulingAction the action performed at scheduling time.
+ */
+ void changeScheduling(String name, String scheduling, String schedulingAction) throws Exception;
+
+ /**
+ * Copy storage of a repository into another repository.
+ *
+ * @param sourceRepository the source repository name.
+ * @param destinationRepository the destination repository name.
+ */
+ void copy(String sourceRepository, String destinationRepository) throws Exception;
+
+ /**
+ * Get the list of existing repositories.
+ *
+ * @return the {@link Collection} of repositories.
+ */
+ Collection<Repository> repositories();
+
+ /**
+ * Get a repository identified by a name.
+ *
+ * @param name the repository name.
+ * @return the corresponding {@link Repository} or {@code null} if it doesn't exist.
+ */
+ Repository repository(String name);
+
+ /**
+ * Add an artifact in the repository identified by the given name.
+ *
+ * @param url the artifact URL.
+ * @param name the repository name.
+ */
+ void addArtifact(String url, String name) throws Exception;
+
+ /**
+ * Add an artifact in the repository identified by the given name, using provided Maven coordinates.
+ *
+ * @param url the artifact URL.
+ * @param groupId the artifact groupId.
+ * @param artifactId the artifact artifactId.
+ * @param version the artifact version.
+ * @param type the artifact type.
+ * @param classifier the artifact classifier (or null).
+ * @param name the repository name.
+ */
+ void addArtifact(String url, String groupId, String artifactId, String version, String type, String classifier, String name) throws Exception;
+
+ /**
+ * Delete an artifact in the given repository.
+ *
+ * @param artifactUrl the artifact URL in the repository (could be relative path or mvn URL).
+ * @param name the repository name.
+ */
+ void deleteArtifact(String artifactUrl, String name) throws Exception;
+
+ /**
+ * Delete an artifact (identified by Maven coordinates) in the given repository.
+ *
+ * @param groupId the artifact groupId.
+ * @param artifactId the artifact artifactId.
+ * @param version the artifact version.
+ * @param type the artifact type.
+ * @param classifier the artifact classifier.
+ * @param name the repository name.
+ */
+ void deleteArtifact(String groupId, String artifactId, String version, String type, String classifier, String name) throws Exception;
+
+ /**
+ * Create/update bundle repository.xml (formerly OBR) for the given repository.
+ *
+ * @param name the repository name.
+ */
+ void updateBundleRepositoryDescriptor(String name) throws Exception;
+
+}
diff --git a/deployer/pom.xml b/repository/pom.xml
similarity index 86%
copy from deployer/pom.xml
copy to repository/pom.xml
index 5d23b7e..0e5f05f 100644
--- a/deployer/pom.xml
+++ b/repository/pom.xml
@@ -24,20 +24,18 @@
<parent>
<groupId>org.apache.karaf</groupId>
<artifactId>cave</artifactId>
- <version>4.1.3-SNAPSHOT</version>
+ <version>4.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>org.apache.karaf.cave</groupId>
- <artifactId>org.apache.karaf.cave.deployer</artifactId>
- <name>Apache Karaf :: Cave :: Deployer</name>
+ <artifactId>org.apache.karaf.cave.repository</artifactId>
+ <name>Apache Karaf :: Cave :: Repository</name>
<packaging>pom</packaging>
<modules>
<module>api</module>
<module>service</module>
- <module>command</module>
- <module>management</module>
</modules>
</project>
\ No newline at end of file
diff --git a/deployer/service/pom.xml b/repository/service/pom.xml
similarity index 59%
copy from deployer/service/pom.xml
copy to repository/service/pom.xml
index 6009585..801e795 100644
--- a/deployer/service/pom.xml
+++ b/repository/service/pom.xml
@@ -23,29 +23,57 @@
<parent>
<groupId>org.apache.karaf.cave</groupId>
- <artifactId>org.apache.karaf.cave.deployer</artifactId>
- <version>4.1.3-SNAPSHOT</version>
+ <artifactId>org.apache.karaf.cave.repository</artifactId>
+ <version>4.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
- <groupId>org.apache.karaf.cave.deployer</groupId>
- <artifactId>org.apache.karaf.cave.deployer.service</artifactId>
- <name>Apache Karaf :: Cave :: Deployer :: Service</name>
+ <groupId>org.apache.karaf.cave.repository</groupId>
+ <artifactId>org.apache.karaf.cave.repository.service</artifactId>
+ <name>Apache Karaf :: Cave :: Repository :: Service</name>
<packaging>bundle</packaging>
<properties>
- <maven.version>3.1.1</maven.version>
<aether.version>1.0.1.v20141111</aether.version>
+ <maven.version>3.1.1</maven.version>
</properties>
<dependencies>
<dependency>
- <groupId>org.apache.karaf.cave.deployer</groupId>
- <artifactId>org.apache.karaf.cave.deployer.api</artifactId>
- <version>${project.version}</version>
+ <groupId>org.osgi</groupId>
+ <artifactId>osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>osgi.cmpn</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.karaf.cave.repository</groupId>
+ <artifactId>org.apache.karaf.cave.repository.api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${servlet.spec.groupId}</groupId>
+ <artifactId>${servlet.spec.artifactId}</artifactId>
+ <version>${servlet.spec.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.url</groupId>
+ <artifactId>pax-url-aether</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.karaf</groupId>
+ <artifactId>org.apache.karaf.util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.karaf.scheduler</groupId>
+ <artifactId>org.apache.karaf.scheduler.core</artifactId>
</dependency>
- <!-- Maven dependencies -->
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
@@ -78,8 +106,6 @@
<artifactId>maven-aether-provider</artifactId>
<version>${maven.version}</version>
</dependency>
-
- <!-- Eclipse Aether -->
<dependency>
<groupId>org.eclipse.aether</groupId>
<artifactId>aether-connector-wagon</artifactId>
@@ -146,28 +172,6 @@
</exclusions>
</dependency>
<dependency>
- <groupId>org.apache.httpcomponents</groupId>
- <artifactId>httpcore-osgi</artifactId>
- <version>4.2.5</version>
- <exclusions>
- <exclusion>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>org.apache.httpcomponents</groupId>
- <artifactId>httpclient-osgi</artifactId>
- <version>4.2.5</version>
- <exclusions>
- <exclusion>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
<groupId>org.eclipse.sisu</groupId>
<artifactId>org.eclipse.sisu.plexus</artifactId>
<version>0.1.1</version>
@@ -200,12 +204,8 @@
<artifactId>jsr305</artifactId>
</exclusion>
<exclusion>
- <artifactId>guava</artifactId>
- <groupId>com.google.guava</groupId>
- </exclusion>
- <exclusion>
- <artifactId>javax.inject</artifactId>
<groupId>javax.inject</groupId>
+ <artifactId>javax.inject</artifactId>
</exclusion>
</exclusions>
</dependency>
@@ -219,66 +219,20 @@
<artifactId>sisu-guava</artifactId>
<version>0.9.9</version>
</dependency>
- <!--
- <dependency>
- <groupId>javax.inject</groupId>
- <artifactId>com.springsource.javax.inject</artifactId>
- <version>1.0.0</version>
- </dependency>
- -->
-
- <!-- Guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
- <!-- OSGi & Karaf-->
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>osgi.core</artifactId>
- </dependency>
<dependency>
- <groupId>org.osgi</groupId>
- <artifactId>osgi.cmpn</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.karaf</groupId>
- <artifactId>org.apache.karaf.util</artifactId>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-frontend-jaxrs</artifactId>
</dependency>
<dependency>
- <groupId>org.apache.karaf.features</groupId>
- <artifactId>org.apache.karaf.features.core</artifactId>
- <version>${karaf.version}</version>
+ <groupId>com.fasterxml.jackson.jaxrs</groupId>
+ <artifactId>jackson-jaxrs-json-provider</artifactId>
</dependency>
-
- <!-- Logging -->
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </dependency>
-
- <!-- Testing -->
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.12</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.ops4j.pax.url</groupId>
- <artifactId>pax-url-aether</artifactId>
- <version>2.4.6</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.25</version>
- <scope>test</scope>
- </dependency>
-
</dependencies>
<build>
@@ -290,82 +244,48 @@
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
- <version>2.5.4</version>
- <extensions>true</extensions>
<inherited>true</inherited>
+ <extensions>true</extensions>
<configuration>
<instructions>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Export-Package>!*</Export-Package>
<Import-Package>
- org.apache.karaf.cave.deployer.api,
- org.slf4j;resolution:=optional,
- org.junit;resolution:=optional,
- org.testng*;resolution:=optional,
- junit.framework.*;resolution:=optional,
- org.apache.maven.model.*;resolution:=optional,
- org.apache.maven.artifact.*;resolution:=optional,
- org.apache.maven.cli.*;resolution:=optional,
- org.apache.maven.settings.merge.*;resolution:=optional,
- org.apache.maven.wagon.events.*;resolution:=optional,
- org.apache.commons.cli.*;resolution:=optional,
- org.apache.tools.ant.*;resolution:=optional,
- org.codehaus.plexus.component.repository.exception.*,
- org.codehaus.plexus.component.annotations.*;resolution:=optional,
- org.codehaus.plexus.util.*;resolution:=optional,
- org.sonatype.plexus.components.cipher.*;resolution:=optional,
- org.sonatype.plexus.components.sec.dispatcher.*;resolution:=optional,
- hudson.maven.*;resolution:=optional,
- org.eclipse.aether.util.repository.layout.*;resolution:=optional,
- org.apache.maven.wagon.*;resolution:=optional,
+ !org.apache.http*,
+ !org.eclipse.aether*,
+ !org.sonatype*,
ch.qos.logback*;resolution:=optional,
+ junit.framework;resolution:=optional,
+ org.junit;resolution:=optional,
+ org.slf4j.impl;resolution:=optional,
+ org.testng.annotations;resolution:=optional,
+ javax.xml.bind*;version="[2,3)",
+ org.osgi.service.repository;resolution:=optional,
+ com.fasterxml.jackson*;version="[2.8,3)",
*
</Import-Package>
<Private-Package>
- org.apache.karaf.cave.deployer.service.impl,
- org.apache.karaf.features.internal.model,
- org.apache.felix.utils.version,
- org.apache.felix.utils.properties,
- org.apache.karaf.util
+ org.apache.karaf.cave.repository.service*,
+ org.apache.karaf.util*,
+ org.apache.maven*,
+ org.eclipse.aether*,
+ com.google*,
+ javax.inject,
+ org.apache.commons.cli,
+ org.apache.http*,
+ org.codehaus.plexus*,
+ org.codehaus.classworlds,
+ org.eclipse.sisu*,
+ javax.enterprise.inject,
+ javax.enterprise.context,
+ javax.enterprise.util,
+ org.sonatype*,
+ org.apache.felix.utils.*,
+ org.apache.felix.resolver,
+ org.apache.felix.resolver.util,
+ org.apache.felix.resolver.reason
</Private-Package>
- <Embed-Dependency>
- sisu-guice,
- sisu-guava,
- sisu-inject-bean,
- javax.interceptor-api,
- javax.el-api,
- cdi-api,
- aether-transport-http,
- aether-transport-file,
- aether-connector-basic,
- wagon-provider-api,
- aether-util,
- aether-spi,
- aether-impl,
- aether-api,
- aether-connector-wagon,
- maven-aether-provider,
- maven-model,
- maven-compat,
- ant,
- maven-settings-builder,
- maven-settings,
- org.eclipse.sisu.plexus,
- org.eclipse.sisu.inject,
- plexus-component-annotations,
- plexus-sec-dispatcher,
- plexus-cipher,
- commons-cli,
- maven-embedder,
- plexus-classworlds,
- maven-model-builder,
- maven-artifact,
- maven-repository-metadata,
- maven-core,
- maven-plugin-api,
- plexus-utils,
- plexus-interpolation
- </Embed-Dependency>
- <Embed-Directory>jars</Embed-Directory>
- <Embed-Transitive>true</Embed-Transitive>
+ <_dsannotations>*</_dsannotations>
</instructions>
</configuration>
</plugin>
diff --git a/repository/service/src/main/java/org/apache/karaf/cave/repository/service/RepositoryServiceImpl.java b/repository/service/src/main/java/org/apache/karaf/cave/repository/service/RepositoryServiceImpl.java
new file mode 100644
index 0000000..37e2900
--- /dev/null
+++ b/repository/service/src/main/java/org/apache/karaf/cave/repository/service/RepositoryServiceImpl.java
@@ -0,0 +1,989 @@
+/*
+ * 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.cave.repository.service;
+
+import org.apache.karaf.cave.repository.Repository;
+import org.apache.karaf.cave.repository.RepositoryService;
+import org.apache.karaf.cave.repository.service.bundlerepository.BundleRepository;
+import org.apache.karaf.cave.repository.service.bundlerepository.ResourceBuilder;
+import org.apache.karaf.cave.repository.service.bundlerepository.ResourceImpl;
+import org.apache.karaf.cave.repository.service.bundlerepository.ResourceUtils;
+import org.apache.karaf.cave.repository.service.maven.ConsoleRepositoryListener;
+import org.apache.karaf.cave.repository.service.maven.ConsoleTransferListener;
+import org.apache.karaf.cave.repository.service.maven.MavenServlet;
+import org.apache.karaf.cave.repository.service.scheduler.RepositoryJob;
+import org.apache.karaf.scheduler.ScheduleOptions;
+import org.apache.karaf.scheduler.Scheduler;
+import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
+import org.eclipse.aether.DefaultRepositorySystemSession;
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
+import org.eclipse.aether.impl.DefaultServiceLocator;
+import org.eclipse.aether.installation.InstallRequest;
+import org.eclipse.aether.repository.LocalRepository;
+import org.eclipse.aether.repository.LocalRepositoryManager;
+import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
+import org.eclipse.aether.spi.connector.transport.TransporterFactory;
+import org.eclipse.aether.transport.file.FileTransporterFactory;
+import org.eclipse.aether.transport.http.HttpTransporterFactory;
+import org.ops4j.pax.url.mvn.MavenResolver;
+import org.ops4j.pax.url.mvn.MavenResolvers;
+import org.osgi.framework.BundleException;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Resource;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.http.HttpService;
+
+import javax.xml.bind.DatatypeConverter;
+import javax.xml.stream.XMLStreamException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.FileAlreadyExistsException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.FileVisitor;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileTime;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import static java.nio.file.StandardCopyOption.ATOMIC_MOVE;
+import static java.util.jar.JarFile.MANIFEST_NAME;
+import static org.osgi.service.repository.ContentNamespace.*;
+
+@Component(
+ name = "org.apache.karaf.cave.repository",
+ immediate = true
+)
+public class RepositoryServiceImpl implements RepositoryService {
+
+ @Reference
+ private HttpService httpService;
+
+ @Reference
+ private Scheduler scheduler;
+
+ private final static Pattern mvnPattern = Pattern.compile("mvn:([^/ ]+)/([^/ ]+)/([^/ ]*)(/([^/ ]+)(/([^/ ]+))?)?");
+
+ private static final String STORAGE_FILE = "repositories.db";
+
+ private File baseStorage;
+ private final Map<String, Repository> repositories = new ConcurrentHashMap<>();
+ private String httpContext;
+
+ @Activate
+ public void activate(ComponentContext componentContext) throws Exception {
+ activate(componentContext.getProperties());
+ }
+
+ /**
+ * Only visible for testing purpose
+ */
+ protected void activate(Dictionary<String, Object> properties) throws Exception {
+ baseStorage = new File((properties.get("storage.location") != null) ? properties.get("storage.location").toString() : System.getProperty("karaf.data") + File.separator + "cave" + File.separator + "repository");
+ httpContext = (properties.get("http.context") != null) ? properties.get("http.context").toString() : "/cave/repository";
+ // load repositories db to populate the map and register the servlet
+ load();
+ for (Repository repository : repositories.values()) {
+ registerMavenServlet(repository);
+ }
+ }
+
+ @Deactivate
+ public void deactivate(ComponentContext componentContext) throws Exception {
+ Dictionary<String, Object> properties = componentContext.getProperties();
+ // unregister repository servlets
+ for (Repository repository : repositories.values()) {
+ unregisterMavenServlet(repository);
+ }
+ }
+
+ @Override
+ public Repository create(String name) throws Exception {
+ return create(name, null);
+ }
+
+ @Override
+ public Repository create(String name, String location) throws Exception {
+ return create(name, location, httpContext + "/" + name, null, false, "karaf", null, null, null, null, 8);
+ }
+
+ @Override
+ public Repository create(String name, String location, String proxy) throws Exception {
+ return create(name, location, httpContext + "/" + name, proxy, false, "karaf", null, null, null, null,8);
+ }
+
+ @Override
+ public Repository create(String name, String location, String proxy, boolean mirror) throws Exception {
+ return create(name, location, httpContext + "/" + name, proxy, mirror, "karaf", null, null, null, null, 8);
+ }
+
+ @Override
+ public Repository create(String name, String location, String url, String proxy, boolean mirror, String realm, String downloadRole, String uploadRole, String scheduling, String schedulingAction, int poolSize) throws Exception {
+ if (name == null || name.isEmpty()) {
+ throw new IllegalArgumentException("Repository name is mandatory");
+ }
+ if (repositories.get(name) != null) {
+ throw new IllegalArgumentException("Repository " + name + " already exists");
+ }
+ if (url == null || url.isEmpty()) {
+ url = httpContext + "/" + name;
+ }
+ if (proxy == null || proxy.isEmpty()) {
+ location = new File(baseStorage, name).getAbsolutePath();
+ }
+ // create the repository storage
+ if (location != null && !location.isEmpty() && !Files.exists(Paths.get(location))) {
+ Files.createDirectories(Paths.get(location));
+ }
+ // create the repository model
+ Repository repository = new Repository();
+ repository.setName(name);
+ repository.setLocation(location);
+ repository.setUrl(url);
+ repository.setProxy(proxy);
+ repository.setMirror(mirror);
+ repository.setRealm(realm);
+ repository.setDownloadRole(downloadRole);
+ repository.setUploadRole(uploadRole);
+ repository.setPoolSize(poolSize);
+ repository.setScheduling(scheduling);
+ repository.setSchedulingAction(schedulingAction);
+ repositories.put(name, repository);
+ // register the repository servlet
+ registerMavenServlet(repository);
+ // optionally register the repository scheduling job
+ scheduleRepository(repository);
+ // update repositories DB
+ save();
+ return repository;
+ }
+
+ @Override
+ public void changeLocation(String name, String location) throws Exception {
+ if (repositories.get(name) == null) {
+ throw new IllegalArgumentException("Repository " + name + " doesn't exist");
+ }
+ Repository repository = repositories.get(name);
+ if (repository.getLocation() != null && !repository.getLocation().isEmpty()) {
+ if (!Files.exists(Paths.get(location))) {
+ Files.createDirectories(Paths.get(location));
+ }
+ final Path source = Paths.get(repository.getLocation());
+ final Path target = Paths.get(location);
+ Files.move(source, target, ATOMIC_MOVE);
+ }
+ if (location != null && !location.isEmpty()) {
+ File locationFile = new File(location);
+ repository.setLocation(locationFile.getAbsolutePath());
+ } else {
+ repository.setLocation(location);
+ }
+ repositories.put(name, repository);
+ save();
+ }
+
+ @Override
+ public void changeUrl(String name, String url) throws Exception {
+ if (repositories.get(name) == null) {
+ throw new IllegalArgumentException("Repository " + name + " doesn't exist");
+ }
+ if (url == null) {
+ throw new IllegalArgumentException("URL can't be null");
+ }
+ Repository repository = repositories.get(name);
+ unregisterMavenServlet(repository);
+ repository.setUrl(url);
+ registerMavenServlet(repository);
+ repositories.put(name, repository);
+ save();
+ }
+
+ @Override
+ public void changeProxy(String name, String proxy, boolean mirror) throws Exception {
+ if (repositories.get(name) == null) {
+ throw new IllegalArgumentException("Repository " + name + " doesn't exist");
+ }
+ Repository repository = repositories.get(name);
+ unregisterMavenServlet(repository);
+ repository.setProxy(proxy);
+ repository.setMirror(mirror);
+ registerMavenServlet(repository);
+ repositories.put(name, repository);
+ save();
+ }
+
+ @Override
+ public void changeSecurity(String name, String realm, String downloadRole, String uploadRole) throws Exception {
+ if (repositories.get(name) == null) {
+ throw new IllegalArgumentException("Repository " + name + " doesn't exist");
+ }
+ Repository repository = repositories.get(name);
+ unregisterMavenServlet(repository);
+ repository.setRealm(realm);
+ repository.setDownloadRole(downloadRole);
+ repository.setUploadRole(uploadRole);
+ registerMavenServlet(repository);
+ repositories.put(name, repository);
+ save();
+ }
+
+ @Override
+ public void changeScheduling(String name, String scheduling, String schedulingAction) throws Exception {
+ if (repositories.get(name) == null) {
+ throw new IllegalArgumentException("Repository " + name + " doesn't exist");
+ }
+ Repository repository = repositories.get(name);
+ unscheduleRepository(repository);
+ repository.setScheduling(scheduling);
+ repository.setSchedulingAction(schedulingAction);
+ scheduleRepository(repository);
+ repositories.put(name, repository);
+ save();
+ }
+
+ @Override
+ public void copy(String sourceRepositoryName, String destinationRepositoryName) throws Exception {
+ if (repositories.get(sourceRepositoryName) == null) {
+ throw new IllegalArgumentException("Repository " + sourceRepositoryName + " doesn't exist");
+ }
+ if (repositories.get(destinationRepositoryName) == null) {
+ throw new IllegalArgumentException("Repository " + destinationRepositoryName + " doesn't exist");
+ }
+ Repository sourceRepository = repositories.get(sourceRepositoryName);
+ Repository destinationRepository = repositories.get(destinationRepositoryName);
+ if (sourceRepository.getLocation() == null || sourceRepository.getLocation().isEmpty()) {
+ throw new IllegalStateException("Source repository " + sourceRepositoryName + " location is not defined");
+ }
+ if (destinationRepository.getLocation() == null || destinationRepository.getLocation().isEmpty()) {
+ throw new IllegalStateException("Destination repository " + destinationRepositoryName + " location is not defined");
+ }
+ final Path source = Paths.get(sourceRepository.getLocation());
+ final Path target = Paths.get(destinationRepository.getLocation());
+ Files.walkFileTree(source, new FileVisitor<Path>() {
+ @Override
+ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
+ Path newDir = target.resolve(source.relativize(dir));
+ try {
+ Files.copy(dir, newDir, StandardCopyOption.COPY_ATTRIBUTES);
+ } catch (FileAlreadyExistsException faee) {
+ // ignore
+ } catch (IOException ioe) {
+ return FileVisitResult.SKIP_SUBTREE;
+ }
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ Files.copy(file, target.resolve(source.relativize(file)), StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING);
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
+ if (exc == null) {
+ Path newDir = target.resolve(source.relativize(dir));
+ FileTime time = Files.getLastModifiedTime(dir);
+ Files.setLastModifiedTime(newDir, time);
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+
+ @Override
+ public void remove(String name) throws Exception {
+ remove(name, false);
+ }
+
+ @Override
+ public void remove(String name, boolean storageCleanup) throws Exception {
+ if (repositories.get(name) == null) {
+ throw new IllegalArgumentException("Repository " + name + " doesn't exist");
+ }
+ Repository repository = repositories.get(name);
+ // cleanup storage
+ if (storageCleanup && repository.getLocation() != null && !repository.getLocation().isEmpty()) {
+ purge(repository);
+ }
+ // unregister repository servlet
+ unregisterMavenServlet(repository);
+ // unschedule
+ unscheduleRepository(repository);
+ // remove the repository from the map and update repositories DB
+ repositories.remove(name);
+ save();
+ }
+
+ @Override
+ public void purge(String name) throws Exception {
+ if (repositories.get(name) == null) {
+ throw new IllegalArgumentException("Repository " + name + " doesn't exist");
+ }
+ purge(repositories.get(name));
+ }
+
+ @Override
+ public synchronized Collection<Repository> repositories() {
+ return repositories.values();
+ }
+
+ @Override
+ public synchronized Repository repository(String name) {
+ return repositories.get(name);
+ }
+
+ @Override
+ public void addArtifact(String artifactUrl, String name) throws Exception {
+ Map<String, String> mavenCoordinates = new HashMap<>();
+ if (isMavenUrl(artifactUrl)) {
+ mavenCoordinates = parseMvnUrl(artifactUrl);
+ } else {
+ int index = artifactUrl.lastIndexOf('.');
+ if (index != -1) {
+ mavenCoordinates.put("extension", artifactUrl.substring(index + 1));
+ int slashIndex = artifactUrl.lastIndexOf('/');
+ if (slashIndex != -1) {
+ mavenCoordinates.put("artifactId", artifactUrl.substring(slashIndex + 1, index));
+ } else {
+ throw new IllegalArgumentException("Can't find possible artifactId in the provided artifact URL");
+ }
+ } else {
+ mavenCoordinates.put("extension", "jar");
+ int slashIndex = artifactUrl.lastIndexOf('/');
+ if (slashIndex != -1) {
+ mavenCoordinates.put("artifactId", artifactUrl.substring(slashIndex + 1));
+ } else {
+ throw new IllegalArgumentException("Can't find possible artifactId in the provided artifact URL");
+ }
+ }
+ }
+ addArtifact(artifactUrl, mavenCoordinates.get("groupId"), mavenCoordinates.get("artifactId"), mavenCoordinates.get("version"), mavenCoordinates.get("extension"), mavenCoordinates.get("classifier"), name);
+ }
+
+ @Override
+ public void addArtifact(String artifactUrl, String groupId, String artifactId, String version, String type, String classifier, String name) throws Exception {
+ if (repositories.get(name) == null) {
+ throw new IllegalArgumentException("Repository " + name + " doesn't exist");
+ }
+ if (artifactUrl == null) {
+ throw new IllegalArgumentException("Artifact URL can't be null");
+ }
+
+ if (repositories.get(name).getLocation() == null || repositories.get(name).getLocation().isEmpty()) {
+ throw new IllegalStateException("Repository " + name + " location is not defined");
+ }
+
+ File artifactFile = File.createTempFile(artifactId, type);
+ try (FileOutputStream os = new FileOutputStream(artifactFile)) {
+ copyStream(new URI(artifactUrl).toURL().openStream(), os);
+ os.flush();
+ }
+
+ DefaultServiceLocator defaultServiceLocator = MavenRepositorySystemUtils.newServiceLocator();
+ defaultServiceLocator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class);
+ defaultServiceLocator.addService(TransporterFactory.class, FileTransporterFactory.class);
+ defaultServiceLocator.addService(TransporterFactory.class, HttpTransporterFactory.class);
+ RepositorySystem repositorySystem = defaultServiceLocator.getService(RepositorySystem.class);
+ DefaultRepositorySystemSession repositorySystemSession = MavenRepositorySystemUtils.newSession();
+ LocalRepository localRepository = new LocalRepository(repositories.get(name).getLocation());
+ LocalRepositoryManager localRepositoryManager = repositorySystem.newLocalRepositoryManager(repositorySystemSession, localRepository);
+ repositorySystemSession.setLocalRepositoryManager(localRepositoryManager);
+ repositorySystemSession.setTransferListener(new ConsoleTransferListener(System.out));
+ repositorySystemSession.setRepositoryListener(new ConsoleRepositoryListener(System.out));
+ Artifact artifact;
+ if (classifier != null) {
+ artifact = new DefaultArtifact(groupId, artifactId, classifier, type, version);
+ } else {
+ artifact = new DefaultArtifact(groupId, artifactId, type, version);
+ }
+ artifact = artifact.setFile(artifactFile);
+
+ InstallRequest installRequest = new InstallRequest();
+ installRequest.addArtifact(artifact);
+ repositorySystem.install(repositorySystemSession, installRequest);
+ }
+
+ /**
+ * Check if an URL is a mvn one or not.
+ * <p>
+ * Visible for testing purpose.
+ *
+ * @param url the URL to check.
+ * @return true if the URL is a mvn URL, false else.
+ */
+ protected static boolean isMavenUrl(String url) {
+ Matcher matcher = mvnPattern.matcher(url);
+ return matcher.matches();
+ }
+
+ /**
+ * Extract Maven coordinates from a given URL.
+ * <p>
+ * Visible for testing purpose.
+ *
+ * @param artifactUrl the artifact URL.
+ * @return the extracted Maven coordinates.
+ */
+ protected static Map<String, String> parseMvnUrl(String artifactUrl) {
+ Matcher matcher = mvnPattern.matcher(artifactUrl);
+ if (!matcher.matches()) {
+ return null;
+ }
+ Map<String, String> result = new HashMap<>();
+ result.put("groupId", matcher.group(1));
+ result.put("artifactId", matcher.group(2));
+ result.put("version", matcher.group(3));
+ if (matcher.group(5) == null) {
+ result.put("extension", "jar");
+ } else {
+ result.put("extension", matcher.group(5));
+ }
+ result.put("classifier", matcher.group(7));
+ return result;
+ }
+
+ static long copyStream(InputStream is, OutputStream os) throws IOException {
+ byte[] buffer = new byte[4096];
+ long count = 0;
+ int n = 0;
+ while (-1 != (n = is.read(buffer))) {
+ os.write(buffer, 0, n);
+ count += n;
+ }
+ return count;
+ }
+
+ @Override
+ public void deleteArtifact(String artifactUrl, String name) throws Exception {
+ if (repositories.get(name) == null) {
+ throw new IllegalArgumentException("Repository " + name + " doesn't exist");
+ }
+ if (repositories.get(name).getLocation() == null || repositories.get(name).getLocation().isEmpty()) {
+ throw new IllegalStateException("Repository " + name + " location is not defined");
+ }
+ Path path;
+ // if the URL is a mvn URL
+ if (artifactUrl.startsWith("mvn:")) {
+ path = Paths.get(repositories.get(name).getLocation()).resolve(Paths.get(convertMvnUrlToPath(artifactUrl)));
+ } else {
+ // the artifact location is relative to the repository storage
+ path = Paths.get(repositories.get(name).getLocation() + "/" + artifactUrl);
+ }
+ if (Files.exists(path)) {
+ if (Files.isDirectory(path)) {
+ Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ Files.delete(file);
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
+ Files.delete(dir);
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ } else {
+ Files.delete(path);
+ }
+ }
+ }
+
+ /**
+ * Visible for testing.
+ */
+ protected static String convertMvnUrlToPath(String mvnUrl) {
+ Map<String, String> coordinates = parseMvnUrl(mvnUrl);
+ return convertMvnCoordinatesToPath(coordinates);
+ }
+
+ /**
+ * Visible for testing.
+ */
+ protected static String convertMvnCoordinatesToPath(Map<String, String> coordinates) {
+ StringBuilder builder = new StringBuilder();
+ if (coordinates.get("groupId") != null) {
+ builder.append(coordinates.get("groupId").replace(".", "/")).append("/");
+ }
+ builder.append(coordinates.get("artifactId")).append("/");
+ builder.append(coordinates.get("version")).append("/");
+ builder.append(coordinates.get("artifactId")).append("-").append(coordinates.get("version"));
+ if (coordinates.get("classifier") != null) {
+ builder.append("-").append(coordinates.get("classifier"));
+ }
+ builder.append(".").append(coordinates.get("extension"));
+ return builder.toString();
+ }
+
+ @Override
+ public void deleteArtifact(String groupId, String artifactId, String version, String type, String classifier, String name) throws Exception {
+ Map<String, String> coordinates = new HashMap<>();
+ coordinates.put("groupId", groupId);
+ coordinates.put("artifactId", artifactId);
+ coordinates.put("version", version);
+ if (type == null) {
+ coordinates.put("extension", "jar");
+ } else {
+ coordinates.put("extension", type);
+ }
+ coordinates.put("classifier", classifier);
+ if (repositories.get(name).getLocation() != null) {
+ Path path = Paths.get(repositories.get(name).getLocation()).resolve(Paths.get(convertMvnCoordinatesToPath(coordinates)));
+ if (Files.exists(path)) {
+ if (Files.isDirectory(path)) {
+ Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ Files.delete(file);
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
+ Files.delete(dir);
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ } else {
+ Files.delete(path);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void updateBundleRepositoryDescriptor(String name) throws Exception {
+ if (repositories.get(name) == null) {
+ throw new IllegalArgumentException("Repository " + name + " doesn't exist");
+ }
+ if (repositories.get(name).getLocation() != null && !repositories.get(name).getLocation().isEmpty()) {
+ Path bundleRepositoryXmlPath = Paths.get(repositories.get(name).getLocation()).resolve("repository.xml");
+ BundleRepository bundleRepository = new BundleRepository(bundleRepositoryXmlPath.toUri().toString(), name);
+ if (!Files.exists(bundleRepositoryXmlPath)) {
+ // init the repository XML
+ try (Writer writer = Files.newBufferedWriter(bundleRepositoryXmlPath, StandardCharsets.UTF_8)) {
+ bundleRepository.writeRepository(writer);
+ }
+ }
+ List<Resource> resources = new ArrayList<>();
+ updateBundleRepositoryDescriptor(new File(repositories.get(name).getLocation()), resources, repositories.get(name).getLocation());
+ addResources(bundleRepository, resources);
+ }
+ }
+
+ private void addResources(BundleRepository repository, List<Resource> resources) throws IOException, XMLStreamException {
+ if (!resources.isEmpty()) {
+ Map<String, Resource> ids = new HashMap<>();
+ for (Resource resource : repository.getResources()) {
+ ids.put(ResourceUtils.getUri(resource), resource);
+ }
+ List<Resource> toAdd = new ArrayList<>();
+ for (Resource resource : resources) {
+ String uri = ResourceUtils.getUri(resource);
+ if (!ids.containsKey(uri)) {
+ toAdd.add(resource);
+ ids.put(uri, resource);
+ }
+ }
+ if (!toAdd.isEmpty()) {
+ repository.addResourcesAndSave(resources);
+ }
+ }
+ }
+
+ private void updateBundleRepositoryDescriptor(File entry, List<Resource> resources, String location) throws Exception {
+ if (entry.isDirectory()) {
+ File[] children = entry.listFiles();
+ if (children != null) {
+ for (File child : children) {
+ updateBundleRepositoryDescriptor(child, resources, location);
+ }
+ }
+ } else {
+ try {
+ URL bundleUrl = entry.toURI().toURL();
+ if (isBundle(bundleUrl.toString())) {
+ ResourceImpl resource = createResource(bundleUrl, location);
+ resources.add(resource);
+ }
+ } catch (BundleException be) {
+ // nothing to do
+ }
+ }
+ }
+
+ private boolean isBundle(String bundleUrl) {
+ return !bundleUrl.matches(".*\\.sha1") && !bundleUrl.matches(".*\\.pom")
+ && !bundleUrl.matches(".*\\.xml") && !bundleUrl.matches(".*\\.repositories")
+ && !bundleUrl.matches(".*\\.properties") && !bundleUrl.matches(".*\\.lastUpdated");
+ }
+
+ private ResourceImpl createResource(URL url, String location) throws BundleException, IOException, NoSuchAlgorithmException {
+ return createResource(url.openConnection(), location);
+ }
+
+ private ResourceImpl createResource(URLConnection urlConnection, String location) throws BundleException, IOException, NoSuchAlgorithmException {
+ return createResource(urlConnection, urlConnection.getURL().toExternalForm(), location, true);
+ }
+
+ private ResourceImpl createResource(URLConnection urlConnection, String uri, String location, boolean readFully) throws BundleException, IOException, NoSuchAlgorithmException {
+ Map<String, String> headers = null;
+ String digest = null;
+ long size = -1;
+ // find headers, estimate length and checksum
+ try (ContentInputStream is = new ContentInputStream(urlConnection.getInputStream())) {
+ ZipInputStream zis = new ZipInputStream(is);
+ ZipEntry entry;
+ while ((entry = zis.getNextEntry()) != null) {
+ if (MANIFEST_NAME.equals(entry.getName())) {
+ Attributes attributes = new Manifest(zis).getMainAttributes();
+ headers = new HashMap<>();
+ for (Map.Entry attr : attributes.entrySet()) {
+ headers.put(attr.getKey().toString(), attr.getValue().toString());
+ }
+ if (!readFully) {
+ break;
+ }
+ }
+ }
+ if (readFully) {
+ digest = is.getDigest();
+ size = is.getSize();
+ }
+ }
+ if (headers == null) {
+ throw new BundleException("Resource " + urlConnection.getURL() + " does not contain a manifest");
+ }
+ // fix the content directive
+ try {
+ ResourceImpl resource = ResourceBuilder.build(uri, headers);
+ for (Capability cap : resource.getCapabilities(null)) {
+ if (cap.getNamespace().equals(CONTENT_NAMESPACE)) {
+ String resourceURI = cap.getAttributes().get(CAPABILITY_URL_ATTRIBUTE).toString();
+ String locationURI = "file:" + location;
+ if (resourceURI.startsWith(locationURI)) {
+ resourceURI = resourceURI.substring(locationURI.length() + 1);
+ cap.getAttributes().put(CAPABILITY_URL_ATTRIBUTE, resourceURI);
+ }
+ if (readFully) {
+ cap.getAttributes().put(CONTENT_NAMESPACE, digest);
+ cap.getAttributes().put(CAPABILITY_SIZE_ATTRIBUTE, size);
+ }
+ cap.getAttributes().put(CAPABILITY_MIME_ATTRIBUTE, "application/vnd.osgi.bundle");
+ break;
+ }
+ }
+ return resource;
+ } catch (BundleException e) {
+ throw new BundleException("Unable to create resource from " + uri + ": " + e.getMessage(), e);
+ }
+ }
+
+ private static class ContentInputStream extends FilterInputStream {
+ final MessageDigest md;
+ long size = 0;
+
+ public ContentInputStream(InputStream is) throws NoSuchAlgorithmException {
+ super(is);
+ md = MessageDigest.getInstance("SHA-256");
+ }
+
+ @Override
+ public int read() throws IOException {
+ int b = super.read();
+ if (b >= 0) {
+ md.update((byte) b);
+ size++;
+ }
+ return b;
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ int length = super.read(b, off, len);
+ if (length > 0) {
+ md.update(b, off, length);
+ this.size += length;
+ }
+ return length;
+ }
+
+ public String getDigest() {
+ byte[] digest = md.digest();
+ StringBuilder builder = new StringBuilder();
+ for (byte b : digest) {
+ builder.append(String.format("%02x", b));
+ }
+ return builder.toString();
+ }
+
+ public long getSize() {
+ return size;
+ }
+
+ }
+
+ /**
+ * Delete (purge) a repository location.
+ *
+ * @param repository the {@link Repository} to purge.
+ */
+ private void purge(Repository repository) throws Exception {
+ if (repository.getLocation() == null || repository.getLocation().isEmpty()) {
+ throw new IllegalStateException("Repository " + repository.getName() + " location is not defined");
+ }
+ if (Files.isDirectory(Paths.get(repository.getLocation()))) {
+ Files.walkFileTree(Paths.get(repository.getLocation()), new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attributes) throws IOException {
+ Files.delete(file);
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(Path dir, IOException ioe) throws IOException {
+ Files.delete(dir);
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+ }
+
+ /**
+ * Save in the repositories DB storage.
+ * <p>
+ * Only visible for testing purpose.
+ */
+ protected synchronized void save() throws Exception {
+ Properties storage = new Properties();
+ storage.setProperty("count", Integer.toString(repositories.values().size()));
+ int i = 0;
+ for (Repository repository : repositories.values()) {
+ storage.setProperty("item." + i + ".name", repository.getName());
+ storage.setProperty("item." + i + ".location", (repository.getLocation() != null) ? repository.getLocation() : "");
+ storage.setProperty("item." + i + ".url", repository.getUrl());
+ storage.setProperty("item." + i + ".proxy", (repository.getProxy() != null) ? repository.getProxy() : "");
+ storage.setProperty("item." + i + ".mirror", (repository.isMirror()) ? "true" : "false");
+ storage.setProperty("item." + i + ".realm", (repository.getRealm() != null) ? repository.getRealm() : "");
+ storage.setProperty("item." + i + ".downloadRole", (repository.getDownloadRole() != null) ? repository.getDownloadRole() : "");
+ storage.setProperty("item." + i + ".uploadRole", (repository.getUploadRole() != null) ? repository.getUploadRole() : "");
+ storage.setProperty("item." + i + ".poolSize", Integer.toString(repository.getPoolSize()));
+ i++;
+ }
+ saveStorage(storage, new File(baseStorage, STORAGE_FILE), "Cave Repositories DB");
+ }
+
+ /**
+ * Load repositories DB storage.
+ * <p>
+ * Only visible for testing purpose.
+ */
+ protected synchronized void load() throws Exception {
+ File storageFile = new File(baseStorage, STORAGE_FILE);
+ Properties storage = loadStorage(storageFile);
+ int count = 0;
+ if (storage.getProperty("count") != null) {
+ count = Integer.parseInt(storage.getProperty("count"));
+ }
+ for (int i = 0; i < count; i++) {
+ String name = storage.getProperty("item." + i + ".name");
+ String location = (storage.getProperty("item." + i + ".location").isEmpty()) ? null : storage.getProperty("item." + i + ".location");
+ String url = storage.getProperty("item." + i + ".url");
+ String proxy = (storage.getProperty("item." + i + ".proxy").isEmpty()) ? null : storage.getProperty("item." + i + ".proxy");
+ boolean mirror = Boolean.parseBoolean(storage.getProperty("item." + i + ".mirror"));
+ String realm = (storage.getProperty("item." + i + ".realm").isEmpty()) ? null : storage.getProperty("item." + i + ".realm");
+ String downloadRole = (storage.getProperty("item." + i + ".downloadRole").isEmpty()) ? null : storage.getProperty("item." + i + ".downloadRole");
+ String uploadRole = (storage.getProperty("item." + i + ".uploadRole").isEmpty()) ? null : storage.getProperty("item." + i + ".uploadRole");
+ int poolSize = Integer.parseInt(storage.getProperty("item." + i + ".poolSize"));
+ Repository repository = new Repository();
+ repository.setName(name);
+ repository.setLocation(location);
+ repository.setUrl(url);
+ repository.setProxy(proxy);
+ repository.setMirror(mirror);
+ repository.setRealm(realm);
+ repository.setDownloadRole(downloadRole);
+ repository.setUploadRole(uploadRole);
+ repository.setPoolSize(poolSize);
+ repositories.put(name, repository);
+ }
+ }
+
+ /**
+ * Write the repositories DB.
+ *
+ * @param properties the repositories storage model.
+ * @param location the repositories DB location.
+ * @param comment a header comment in the DB file.
+ */
+ private void saveStorage(Properties properties, File location, String comment) throws Exception {
+ if (!location.exists()) {
+ location.getParentFile().mkdirs();
+ location.createNewFile();
+ }
+ try (OutputStream outputStream = new FileOutputStream(location)) {
+ properties.store(outputStream, comment);
+ }
+ }
+
+ /**
+ * Load the repositories DB.
+ *
+ * @param location the repositories DB location.
+ * @return the repositories storage model.
+ */
+ private Properties loadStorage(File location) throws Exception {
+ Properties properties = new Properties();
+ if (location.exists()) {
+ try (InputStream inputStream = new FileInputStream(location)) {
+ properties.load(inputStream);
+ }
+ }
+ return properties;
+ }
+
+ /**
+ * Register a Maven Servlet in the HTTP Service for the given repository.
+ *
+ * @param repository the {@link Repository} to publish.
+ */
+ private void registerMavenServlet(Repository repository) throws Exception {
+ Hashtable<String, String> mavenResolverConfig = new Hashtable<>();
+ mavenResolverConfig.put("defaultRepositories", "file:" + repository.getLocation() + "@id=" + repository.getName() + "@snapshots@releases");
+ mavenResolverConfig.put("defaultLocalRepoAsRemote", "false");
+ mavenResolverConfig.put("useFallbackRepositories", "false");
+ if (repository.getProxy() == null || repository.getProxy().isEmpty() || repository.isMirror()) {
+ if (repository.getLocation() != null && !repository.getLocation().isEmpty()) {
+ mavenResolverConfig.put("localRepository", repository.getLocation());
+ }
+ }
+ if (repository.getProxy() != null && !repository.getProxy().isEmpty()) {
+ mavenResolverConfig.put("repositories", repository.getProxy() + ",file:" + repository.getLocation() + "@id=" + repository.getName() + "@snapshots");
+ } else {
+ mavenResolverConfig.put("repositories", "file:" + repository.getLocation() + "@id=" + repository.getName() + "@snapshots");
+ }
+ MavenResolver mavenResolver = MavenResolvers.createMavenResolver(mavenResolverConfig, null);
+ MavenServlet mavenServlet = new MavenServlet(mavenResolver, repository.getName(), repository.getLocation(), repository.getPoolSize(), repository.getRealm(), repository.getDownloadRole(), repository.getUploadRole());
+ httpService.registerServlet(repository.getUrl(), mavenServlet, null, null);
+ }
+
+ /**
+ * Register repository scheduling in the scheduler service.
+ *
+ * @param repository the repository to schedule.
+ */
+ private void scheduleRepository(Repository repository) throws Exception {
+ if (repository.getScheduling() != null) {
+ ScheduleOptions scheduleOptions;
+ if (repository.getScheduling().contains(":")) {
+ String[] schedule = repository.getScheduling().split(":");
+ if (schedule[0].equalsIgnoreCase("cron")) {
+ scheduleOptions = scheduler.EXPR(schedule[1]);
+ } else if (schedule[0].equalsIgnoreCase("at")) {
+ scheduleOptions = scheduler.AT(DatatypeConverter.parseDateTime(schedule[1]).getTime());
+ } else {
+ throw new IllegalStateException("Unknown scheduling definition: " + repository.getScheduling());
+ }
+ } else {
+ scheduleOptions = scheduler.EXPR(repository.getScheduling());
+ }
+ scheduleOptions.name("cave-repository-" + repository.getName());
+ scheduler.schedule(new RepositoryJob(this, repository), scheduleOptions);
+ }
+ }
+
+ private void unscheduleRepository(Repository repository) throws Exception {
+ if (scheduler != null && scheduler.getJobs() != null) {
+ if (scheduler.getJobs().get("cave-repository-" + repository.getName()) != null) {
+ scheduler.unschedule("cave-repository-" + repository.getName());
+ }
+ }
+ }
+
+ /**
+ * Unregister the Maven servlet for a repository.
+ *
+ * @param repository the {@link Repository}.
+ */
+ private void unregisterMavenServlet(Repository repository) {
+ httpService.unregister(repository.getUrl());
+ }
+
+ /**
+ * Only visible for testing purpose.
+ */
+ protected void setHttpService(HttpService httpService) {
+ this.httpService = httpService;
+ }
+
+ /**
+ * Only visible for testing purpose.
+ */
+ protected void setScheduler(Scheduler scheduler) {
+ this.scheduler = scheduler;
+ }
+
+ /**
+ * Only visible for testing purpose.
+ */
+ protected void clear() {
+ repositories.clear();
+ }
+}
diff --git a/repository/service/src/main/java/org/apache/karaf/cave/repository/service/bundlerepository/BaseClause.java b/repository/service/src/main/java/org/apache/karaf/cave/repository/service/bundlerepository/BaseClause.java
new file mode 100644
index 0000000..1217e6d
--- /dev/null
+++ b/repository/service/src/main/java/org/apache/karaf/cave/repository/service/bundlerepository/BaseClause.java
@@ -0,0 +1,109 @@
+/*
+ * 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.cave.repository.service.bundlerepository;
+
+import org.osgi.framework.Version;
+import org.osgi.resource.Resource;
+
+import java.util.Map;
+
+public abstract class BaseClause {
+
+ public abstract Resource getResource();
+
+ public abstract String getNamespace();
+
+ public abstract Map<String, String> getDirectives();
+
+ public abstract Map<String, Object> getAttributes();
+
+ @Override
+ public String toString() {
+ return toString(getResource(), getNamespace(), getAttributes(), getDirectives());
+ }
+
+ public static String toString(Resource res, String namespace, Map<String, Object> attrs, Map<String, String> dirs) {
+ StringBuilder sb = new StringBuilder();
+ if (res != null) {
+ sb.append("[").append(res).append("]");
+ }
+ sb.append(namespace);
+ for (String key : attrs.keySet()) {
+ sb.append("; ");
+ append(sb, key, attrs.get(key), true);
+ }
+ for (String key : dirs.keySet()) {
+ sb.append("; ");
+ append(sb, key, dirs.get(key), false);
+ }
+ return sb.toString();
+ }
+
+ private static void append(StringBuilder sb, String key, Object val, boolean attribute) {
+ sb.append(key);
+ if (val instanceof Version) {
+ sb.append(":Version=").append(val);
+ } else if (val instanceof Long) {
+ sb.append(":Long=").append(val);
+ } else if (val instanceof Double) {
+ sb.append(":Double=").append(val);
+ } else if (val instanceof Iterable) {
+ Iterable it = (Iterable) val;
+ String scalar = null;
+ for (Object o : it) {
+ String ts;
+ if (o instanceof String) {
+ ts = "String";
+ } else if (o instanceof Long) {
+ ts = "Long";
+ } else if (o instanceof Double) {
+ ts = "Double";
+ } else if (o instanceof Version) {
+ ts = "Version";
+ } else {
+ throw new IllegalArgumentException("Unsupported scalar type: " + o);
+ }
+ if (scalar == null) {
+ scalar = ts;
+ } else if (!scalar.equals(ts)) {
+ throw new IllegalArgumentException("Inconsistent list type for attribute " + key);
+ }
+ }
+ sb.append(":List<").append(scalar).append(">=");
+ sb.append("\"");
+ boolean first = true;
+ for (Object o : it) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(",");
+ }
+ sb.append(o.toString().replace("\"", "\\\"").replace(",", "\\,"));
+ }
+ sb.append("\"");
+ } else {
+ sb.append(attribute ? "=" : ":=");
+ String s = val.toString();
+ if (s.matches("[0-9a-zA-Z_\\-.]*")) {
+ sb.append(s);
+ } else {
+ sb.append("\"").append(s.replace("\"", "\\\\")).append("\"");
+ }
+ }
+ }
+
+}
diff --git a/repository/service/src/main/java/org/apache/karaf/cave/repository/service/bundlerepository/BaseRepository.java b/repository/service/src/main/java/org/apache/karaf/cave/repository/service/bundlerepository/BaseRepository.java
new file mode 100644
index 0000000..801de9f
--- /dev/null
+++ b/repository/service/src/main/java/org/apache/karaf/cave/repository/service/bundlerepository/BaseRepository.java
@@ -0,0 +1,105 @@
+/*
+ * 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.cave.repository.service.bundlerepository;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.osgi.framework.Constants;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+import org.osgi.service.repository.ExpressionCombiner;
+import org.osgi.service.repository.Repository;
+import org.osgi.service.repository.RequirementBuilder;
+import org.osgi.service.repository.RequirementExpression;
+import org.osgi.util.promise.Promise;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BaseRepository implements Repository {
+
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+ protected final List<Resource> resources;
+ protected final Map<String, CapabilitySet> capSets;
+
+ public BaseRepository() {
+ this.resources = new ArrayList<Resource>();
+ this.capSets = new HashMap<String, CapabilitySet>();
+ }
+
+ public BaseRepository(Collection<Resource> resources) {
+ this();
+ for (Resource resource : resources) {
+ addResource(resource);
+ }
+ }
+
+ protected void addResource(Resource resource) {
+ for (Capability cap : resource.getCapabilities(null)) {
+ String ns = cap.getNamespace();
+ capSets.computeIfAbsent(ns, n -> new CapabilitySet(Collections.singletonList(n))).addCapability(cap);
+ }
+ resources.add(resource);
+ }
+
+ public List<Resource> getResources() {
+ return resources;
+ }
+
+ @Override
+ public Map<Requirement, Collection<Capability>> findProviders(Collection<? extends Requirement> requirements) {
+ Map<Requirement, Collection<Capability>> result = new HashMap<Requirement, Collection<Capability>>();
+ for (Requirement requirement : requirements) {
+ CapabilitySet set = capSets.get(requirement.getNamespace());
+ if (set != null) {
+ SimpleFilter sf;
+ if (requirement instanceof RequirementImpl) {
+ sf = ((RequirementImpl) requirement).getFilter();
+ } else {
+ String filter = requirement.getDirectives().get(Constants.FILTER_DIRECTIVE);
+ sf = (filter != null)
+ ? SimpleFilter.parse(filter)
+ : new SimpleFilter(null, null, SimpleFilter.MATCH_ALL);
+ }
+ result.put(requirement, set.match(sf, true));
+ } else {
+ result.put(requirement, Collections.<Capability>emptyList());
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public Promise<Collection<Resource>> findProviders(RequirementExpression expression) {
+ return null;
+ }
+
+ @Override
+ public ExpressionCombiner getExpressionCombiner() {
+ return null;
+ }
+
+ @Override
+ public RequirementBuilder newRequirementBuilder(String namespace) {
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/server/storage/src/main/java/org/apache/karaf/cave/server/storage/OsgiRepository.java b/repository/service/src/main/java/org/apache/karaf/cave/repository/service/bundlerepository/BundleRepository.java
similarity index 88%
rename from server/storage/src/main/java/org/apache/karaf/cave/server/storage/OsgiRepository.java
rename to repository/service/src/main/java/org/apache/karaf/cave/repository/service/bundlerepository/BundleRepository.java
index d7a25de..1c605a7 100644
--- a/server/storage/src/main/java/org/apache/karaf/cave/server/storage/OsgiRepository.java
+++ b/repository/service/src/main/java/org/apache/karaf/cave/repository/service/bundlerepository/BundleRepository.java
@@ -14,8 +14,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.karaf.cave.server.storage;
+package org.apache.karaf.cave.repository.service.bundlerepository;
+import org.osgi.resource.Resource;
+
+import javax.xml.stream.XMLStreamException;
import java.io.IOException;
import java.io.Writer;
import java.net.URI;
@@ -24,17 +27,11 @@ import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
-import javax.xml.stream.XMLStreamException;
-
-import org.apache.karaf.features.internal.repository.StaxParser;
-import org.apache.karaf.features.internal.repository.XmlRepository;
-import org.osgi.resource.Resource;
-
-public class OsgiRepository extends XmlRepository {
+public class BundleRepository extends XmlRepository {
OsgiLoader loader;
- public OsgiRepository(String url, String name) {
+ public BundleRepository(String url, String name) {
this(url);
StaxParser.XmlRepository repository = new StaxParser.XmlRepository();
repository.name = name;
@@ -42,7 +39,7 @@ public class OsgiRepository extends XmlRepository {
getLoaders().put(url, loader);
}
- public OsgiRepository(String url) {
+ public BundleRepository(String url) {
super(url, -1, false);
}
@@ -56,7 +53,7 @@ public class OsgiRepository extends XmlRepository {
}
private void load() {
- // Force repository load
+ // force repository load
getResources();
}
@@ -78,6 +75,7 @@ public class OsgiRepository extends XmlRepository {
}
protected static class OsgiLoader extends XmlLoader {
+
public OsgiLoader(String url) {
super(url, -1);
}
diff --git a/repository/service/src/main/java/org/apache/karaf/cave/repository/service/bundlerepository/CapabilityImpl.java b/repository/service/src/main/java/org/apache/karaf/cave/repository/service/bundlerepository/CapabilityImpl.java
new file mode 100644
index 0000000..5259a6b
--- /dev/null
+++ b/repository/service/src/main/java/org/apache/karaf/cave/repository/service/bundlerepository/CapabilityImpl.java
@@ -0,0 +1,83 @@
+/*
+ * 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.cave.repository.service.bundlerepository;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.osgi.framework.Constants;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Resource;
+
+public class CapabilityImpl extends BaseClause implements Capability {
+
+ private final Resource resource;
+ private final String namespace;
+ private final Map<String, String> dirs;
+ private final Map<String, Object> attrs;
+ private final Set<String> mandatory;
+
+ public CapabilityImpl(Resource resource, String namespace,
+ Map<String, String> dirs, Map<String, Object> attrs) {
+ this.namespace = namespace;
+ this.resource = resource;
+ this.dirs = dirs;
+ this.attrs = attrs;
+
+ // Handle mandatory directive
+ Set<String> mandatory = Collections.emptySet();
+ String value = this.dirs.get(Constants.MANDATORY_DIRECTIVE);
+ if (value != null) {
+ List<String> names = ResourceBuilder.parseDelimitedString(value, ",");
+ mandatory = new HashSet<>(names.size());
+ for (String name : names) {
+ // If attribute exists, then record it as mandatory.
+ if (this.attrs.containsKey(name)) {
+ mandatory.add(name);
+ // Otherwise, report an error.
+ } else {
+ throw new IllegalArgumentException("Mandatory attribute '" + name + "' does not exist.");
+ }
+ }
+ }
+ this.mandatory = mandatory;
+ }
+
+ public Resource getResource() {
+ return resource;
+ }
+
+ public String getNamespace() {
+ return namespace;
+ }
+
+ public Map<String, String> getDirectives() {
+ return dirs;
+ }
+
+ public Map<String, Object> getAttributes() {
+ return attrs;
+ }
+
+ public boolean isAttributeMandatory(String name) {
+ return !mandatory.isEmpty() && mandatory.contains(name);
+ }
+
+}
\ No newline at end of file
diff --git a/repository/service/src/main/java/org/apache/karaf/cave/repository/service/bundlerepository/CapabilitySet.java b/repository/service/src/main/java/org/apache/karaf/cave/repository/service/bundlerepository/CapabilitySet.java
new file mode 100644
index 0000000..0dd28cd
--- /dev/null
+++ b/repository/service/src/main/java/org/apache/karaf/cave/repository/service/bundlerepository/CapabilitySet.java
@@ -0,0 +1,463 @@
+/*
+ * 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.cave.repository.service.bundlerepository;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.apache.felix.utils.version.VersionTable;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+import org.osgi.resource.Capability;
+
+public class CapabilitySet {
+
+ private static final Class<?>[] STRING_CLASS = new Class[] {String.class};
+
+ private final Map<String, Map<Object, Set<Capability>>> indices;
+ private final Set<Capability> capSet = new HashSet<>();
+
+ public CapabilitySet(List<String> indexProps) {
+ indices = new TreeMap<>();
+ for (int i = 0; (indexProps != null) && (i < indexProps.size()); i++) {
+ indices.put(
+ indexProps.get(i), new HashMap<Object, Set<Capability>>());
+ }
+ }
+
+ public void dump() {
+ for (Entry<String, Map<Object, Set<Capability>>> entry : indices.entrySet()) {
+ boolean header1 = false;
+ for (Entry<Object, Set<Capability>> entry2 : entry.getValue().entrySet()) {
+ boolean header2 = false;
+ for (Capability cap : entry2.getValue()) {
+ if (!header1) {
+ System.out.println(entry.getKey() + ":");
+ header1 = true;
+ }
+ if (!header2) {
+ System.out.println(" " + entry2.getKey());
+ header2 = true;
+ }
+ System.out.println(" " + cap);
+ }
+ }
+ }
+ }
+
+ public void addCapability(Capability cap) {
+ capSet.add(cap);
+
+ // Index capability.
+ for (Entry<String, Map<Object, Set<Capability>>> entry : indices.entrySet()) {
+ Object value = cap.getAttributes().get(entry.getKey());
+ if (value != null) {
+ if (value.getClass().isArray()) {
+ value = convertArrayToList(value);
+ }
+
+ Map<Object, Set<Capability>> index = entry.getValue();
+
+ if (value instanceof Collection) {
+ Collection c = (Collection) value;
+ for (Object o : c) {
+ indexCapability(index, cap, o);
+ }
+ } else {
+ indexCapability(index, cap, value);
+ }
+ }
+ }
+ }
+
+ private void indexCapability(
+ Map<Object, Set<Capability>> index, Capability cap, Object capValue) {
+ index.computeIfAbsent(capValue, k -> new HashSet<>()).add(cap);
+ }
+
+ public void removeCapability(Capability cap) {
+ if (capSet.remove(cap)) {
+ for (Entry<String, Map<Object, Set<Capability>>> entry : indices.entrySet()) {
+ Object value = cap.getAttributes().get(entry.getKey());
+ if (value != null) {
+ if (value.getClass().isArray()) {
+ value = convertArrayToList(value);
+ }
+
+ Map<Object, Set<Capability>> index = entry.getValue();
+
+ if (value instanceof Collection) {
+ Collection c = (Collection) value;
+ for (Object o : c) {
+ deindexCapability(index, cap, o);
+ }
+ } else {
+ deindexCapability(index, cap, value);
+ }
+ }
+ }
+ }
+ }
+
+ private void deindexCapability(
+ Map<Object, Set<Capability>> index, Capability cap, Object value) {
+ Set<Capability> caps = index.get(value);
+ if (caps != null) {
+ caps.remove(cap);
+ if (caps.isEmpty()) {
+ index.remove(value);
+ }
+ }
+ }
+
+ public Set<Capability> match(SimpleFilter sf, boolean obeyMandatory) {
+ Set<Capability> matches = match(capSet, sf);
+ return obeyMandatory
+ ? matchMandatory(matches, sf)
+ : matches;
+ }
+
+ @SuppressWarnings("unchecked")
+ private Set<Capability> match(Set<Capability> caps, SimpleFilter sf) {
+ Set<Capability> matches = new HashSet<>();
+
+ if (sf.getOperation() == SimpleFilter.MATCH_ALL) {
+ matches.addAll(caps);
+ } else if (sf.getOperation() == SimpleFilter.AND) {
+ // Evaluate each subfilter against the remaining capabilities.
+ // For AND we calculate the intersection of each subfilter.
+ // We can short-circuit the AND operation if there are no
+ // remaining capabilities.
+ List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+ for (int i = 0; (caps.size() > 0) && (i < sfs.size()); i++) {
+ matches = match(caps, sfs.get(i));
+ caps = matches;
+ }
+ } else if (sf.getOperation() == SimpleFilter.OR) {
+ // Evaluate each subfilter against the remaining capabilities.
+ // For OR we calculate the union of each subfilter.
+ List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+ for (SimpleFilter sf1 : sfs) {
+ matches.addAll(match(caps, sf1));
+ }
+ } else if (sf.getOperation() == SimpleFilter.NOT) {
+ // Evaluate each subfilter against the remaining capabilities.
+ // For OR we calculate the union of each subfilter.
+ matches.addAll(caps);
+ List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+ for (SimpleFilter sf1 : sfs) {
+ matches.removeAll(match(caps, sf1));
+ }
+ } else {
+ Map<Object, Set<Capability>> index = indices.get(sf.getName());
+ if ((sf.getOperation() == SimpleFilter.EQ) && (index != null)) {
+ Set<Capability> existingCaps = index.get(sf.getValue());
+ if (existingCaps != null) {
+ matches.addAll(existingCaps);
+ matches.retainAll(caps);
+ }
+ } else {
+ for (Capability cap : caps) {
+ Object lhs = cap.getAttributes().get(sf.getName());
+ if (lhs != null) {
+ if (compare(lhs, sf.getValue(), sf.getOperation())) {
+ matches.add(cap);
+ }
+ }
+ }
+ }
+ }
+
+ return matches;
+ }
+
+ public static boolean matches(Capability cap, SimpleFilter sf) {
+ return matchesInternal(cap, sf) && matchMandatory(cap, sf);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static boolean matchesInternal(Capability cap, SimpleFilter sf) {
+ boolean matched = true;
+
+ if (sf.getOperation() == SimpleFilter.MATCH_ALL) {
+ matched = true;
+ } else if (sf.getOperation() == SimpleFilter.AND) {
+ // Evaluate each subfilter against the remaining capabilities.
+ // For AND we calculate the intersection of each subfilter.
+ // We can short-circuit the AND operation if there are no
+ // remaining capabilities.
+ List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+ for (int i = 0; matched && (i < sfs.size()); i++) {
+ matched = matchesInternal(cap, sfs.get(i));
+ }
+ } else if (sf.getOperation() == SimpleFilter.OR) {
+ // Evaluate each subfilter against the remaining capabilities.
+ // For OR we calculate the union of each subfilter.
+ matched = false;
+ List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+ for (int i = 0; !matched && (i < sfs.size()); i++) {
+ matched = matchesInternal(cap, sfs.get(i));
+ }
+ } else if (sf.getOperation() == SimpleFilter.NOT) {
+ // Evaluate each subfilter against the remaining capabilities.
+ // For OR we calculate the union of each subfilter.
+ List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+ for (SimpleFilter sf1 : sfs) {
+ matched = !(matchesInternal(cap, sf1));
+ }
+ } else {
+ matched = false;
+ Object lhs = cap.getAttributes().get(sf.getName());
+ if (lhs != null) {
+ matched = compare(lhs, sf.getValue(), sf.getOperation());
+ }
+ }
+
+ return matched;
+ }
+
+ private static Set<Capability> matchMandatory(
+ Set<Capability> caps, SimpleFilter sf) {
+ for (Iterator<Capability> it = caps.iterator(); it.hasNext();) {
+ Capability cap = it.next();
+ if (!matchMandatory(cap, sf)) {
+ it.remove();
+ }
+ }
+ return caps;
+ }
+
+ private static boolean matchMandatory(Capability cap, SimpleFilter sf) {
+ if (cap instanceof CapabilityImpl) {
+ for (Entry<String, Object> entry : cap.getAttributes().entrySet()) {
+ if (((CapabilityImpl) cap).isAttributeMandatory(entry.getKey())
+ && !matchMandatoryAttribute(entry.getKey(), sf)) {
+ return false;
+ }
+ }
+ } else {
+ String value = cap.getDirectives().get(Constants.MANDATORY_DIRECTIVE);
+ if (value != null) {
+ List<String> names = ResourceBuilder.parseDelimitedString(value, ",");
+ for (Entry<String, Object> entry : cap.getAttributes().entrySet()) {
+ if (names.contains(entry.getKey())
+ && !matchMandatoryAttribute(entry.getKey(), sf)) {
+ return false;
+ }
+ }
+ }
+
+ }
+ return true;
+ }
+
+ private static boolean matchMandatoryAttribute(String attrName, SimpleFilter sf) {
+ if ((sf.getName() != null) && sf.getName().equals(attrName)) {
+ return true;
+ } else if (sf.getOperation() == SimpleFilter.AND) {
+ List list = (List) sf.getValue();
+ for (Object aList : list) {
+ SimpleFilter sf2 = (SimpleFilter) aList;
+ if ((sf2.getName() != null)
+ && sf2.getName().equals(attrName)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static boolean compare(Object lhs, Object rhsUnknown, int op) {
+ if (lhs == null) {
+ return false;
+ }
+
+ // If this is a PRESENT operation, then just return true immediately
+ // since we wouldn't be here if the attribute wasn't present.
+ if (op == SimpleFilter.PRESENT) {
+ return true;
+ }
+
+ // If the type is comparable, then we can just return the
+ // result immediately.
+ if (lhs instanceof Comparable) {
+ // Spec says SUBSTRING is false for all types other than string.
+ if ((op == SimpleFilter.SUBSTRING) && !(lhs instanceof String)) {
+ return false;
+ }
+
+ Object rhs;
+ if (op == SimpleFilter.SUBSTRING) {
+ rhs = rhsUnknown;
+ } else {
+ try {
+ rhs = coerceType(lhs, (String) rhsUnknown);
+ } catch (Exception ex) {
+ return false;
+ }
+ }
+
+ switch (op) {
+ case SimpleFilter.EQ:
+ try {
+ return ((Comparable) lhs).compareTo(rhs) == 0;
+ } catch (Exception ex) {
+ return false;
+ }
+ case SimpleFilter.GTE:
+ try {
+ return ((Comparable) lhs).compareTo(rhs) >= 0;
+ } catch (Exception ex) {
+ return false;
+ }
+ case SimpleFilter.LTE:
+ try {
+ return ((Comparable) lhs).compareTo(rhs) <= 0;
+ } catch (Exception ex) {
+ return false;
+ }
+ case SimpleFilter.APPROX:
+ return compareApproximate(lhs, rhs);
+ case SimpleFilter.SUBSTRING:
+ return SimpleFilter.compareSubstring((List<String>) rhs, (String) lhs);
+ default:
+ throw new RuntimeException("Unknown comparison operator: " + op);
+ }
+ }
+
+ // If the LHS is not a comparable or boolean, check if it is an
+ // array. If so, convert it to a list so we can treat it as a
+ // collection.
+ if (lhs.getClass().isArray()) {
+ lhs = convertArrayToList(lhs);
+ }
+
+ // If LHS is a collection, then call compare() on each element
+ // of the collection until a match is found.
+ if (lhs instanceof Collection) {
+ for (Object o : (Collection) lhs) {
+ if (compare(o, rhsUnknown, op)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // Spec says SUBSTRING is false for all types other than string.
+ if (op == SimpleFilter.SUBSTRING) {
+ return false;
+ }
+
+ // Since we cannot identify the LHS type, then we can only perform
+ // equality comparison.
+ try {
+ return lhs.equals(coerceType(lhs, (String) rhsUnknown));
+ } catch (Exception ex) {
+ return false;
+ }
+ }
+
+ private static boolean compareApproximate(Object lhs, Object rhs) {
+ if (rhs instanceof String) {
+ return removeWhitespace((String) lhs)
+ .equalsIgnoreCase(removeWhitespace((String) rhs));
+ } else if (rhs instanceof Character) {
+ return Character.toLowerCase((Character) lhs)
+ == Character.toLowerCase((Character) rhs);
+ }
+ return lhs.equals(rhs);
+ }
+
+ private static String removeWhitespace(String s) {
+ StringBuilder sb = new StringBuilder(s.length());
+ for (int i = 0; i < s.length(); i++) {
+ if (!Character.isWhitespace(s.charAt(i))) {
+ sb.append(s.charAt(i));
+ }
+ }
+ return sb.toString();
+ }
+
+ private static Object coerceType(Object lhs, String rhsString) throws Exception {
+ // If the LHS expects a string, then we can just return
+ // the RHS since it is a string.
+ if (lhs.getClass() == rhsString.getClass()) {
+ return rhsString;
+ }
+
+ // Try to convert the RHS type to the LHS type by using
+ // the string constructor of the LHS class, if it has one.
+ Object rhs;
+ try {
+ if (lhs instanceof Version) {
+ rhs = VersionTable.getVersion(rhsString, false);
+ } else
+ // The Character class is a special case, since its constructor
+ // does not take a string, so handle it separately.
+ if (lhs instanceof Character) {
+ rhs = rhsString.charAt(0);
+ } else {
+ // Spec says we should trim number types.
+ if ((lhs instanceof Number) || (lhs instanceof Boolean)) {
+ rhsString = rhsString.trim();
+ }
+ Constructor ctor = lhs.getClass().getConstructor(STRING_CLASS);
+ ctor.setAccessible(true);
+ rhs = ctor.newInstance(rhsString);
+ }
+ } catch (Exception ex) {
+ throw new Exception(
+ "Could not instantiate class "
+ + lhs.getClass().getName()
+ + " from string constructor with argument '"
+ + rhsString + "' because " + ex
+ );
+ }
+
+ return rhs;
+ }
+
+ /**
+ * This is an ugly utility method to convert an array of primitives
+ * to an array of primitive wrapper objects. This method simplifies
+ * processing LDAP filters since the special case of primitive arrays
+ * can be ignored.
+ *
+ * @param array An array of primitive types.
+ * @return An corresponding array using pritive wrapper objects.
+ */
+ private static List<Object> convertArrayToList(Object array) {
+ int len = Array.getLength(array);
+ List<Object> list = new ArrayList<>(len);
+ for (int i = 0; i < len; i++) {
+ list.add(Array.get(array, i));
+ }
+ return list;
+ }
+}
\ No newline at end of file
diff --git a/repository/service/src/main/java/org/apache/karaf/cave/repository/service/bundlerepository/RequirementImpl.java b/repository/service/src/main/java/org/apache/karaf/cave/repository/service/bundlerepository/RequirementImpl.java
new file mode 100644
index 0000000..645e127
--- /dev/null
+++ b/repository/service/src/main/java/org/apache/karaf/cave/repository/service/bundlerepository/RequirementImpl.java
@@ -0,0 +1,81 @@
+/*
+ * 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.cave.repository.service.bundlerepository;
+
+import java.util.Map;
+
+import org.osgi.framework.Constants;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+
+public class RequirementImpl extends BaseClause implements Requirement {
+
+ private final Resource resource;
+ private final String namespace;
+ private final SimpleFilter filter;
+ private final boolean optional;
+ private final Map<String, String> dirs;
+ private final Map<String, Object> attrs;
+
+ public RequirementImpl(
+ Resource resource, String namespace,
+ Map<String, String> dirs, Map<String, Object> attrs, SimpleFilter filter) {
+ this.resource = resource;
+ this.namespace = namespace;
+ this.dirs = dirs;
+ this.attrs = attrs;
+ this.filter = filter;
+ // Find resolution import directives.
+ optional = Constants.RESOLUTION_OPTIONAL.equals(this.dirs.get(Constants.RESOLUTION_DIRECTIVE));
+ }
+
+ public RequirementImpl(
+ Resource resource, String namespace,
+ Map<String, String> dirs, Map<String, Object> attrs) {
+ this(resource, namespace, dirs, attrs, SimpleFilter.convert(attrs));
+ }
+
+ public String getNamespace() {
+ return namespace;
+ }
+
+ public Map<String, String> getDirectives() {
+ return dirs;
+ }
+
+ public Map<String, Object> getAttributes() {
+ return attrs;
+ }
+
+ public Resource getResource() {
+ return resource;
+ }
+
+ public boolean matches(Capability cap) {
+ return CapabilitySet.matches(cap, getFilter());
+ }
+
+ public boolean isOptional() {
+ return optional;
+ }
+
+ public SimpleFilter getFilter() {
+ return filter;
+ }
+
+}
\ No newline at end of file
diff --git a/repository/service/src/main/java/org/apache/karaf/cave/repository/service/bundlerepository/ResourceBuilder.java b/repository/service/src/main/java/org/apache/karaf/cave/repository/service/bundlerepository/ResourceBuilder.java
new file mode 100644
index 0000000..4f995d9
--- /dev/null
+++ b/repository/service/src/main/java/org/apache/karaf/cave/repository/service/bundlerepository/ResourceBuilder.java
@@ -0,0 +1,1203 @@
+/*
+ * 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.cave.repository.service.bundlerepository;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.utils.version.VersionRange;
+import org.apache.felix.utils.version.VersionTable;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+import org.osgi.framework.namespace.ExecutionEnvironmentNamespace;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.namespace.service.ServiceNamespace;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+import org.osgi.service.repository.ContentNamespace;
+
+public final class ResourceBuilder {
+
+ public static final String RESOLUTION_DYNAMIC = "dynamic";
+
+ private static final char EOF = (char) -1;
+
+ private static final int CLAUSE_START = 0;
+ private static final int PARAMETER_START = 1;
+ private static final int KEY = 2;
+ private static final int DIRECTIVE_OR_TYPEDATTRIBUTE = 4;
+ private static final int ARGUMENT = 8;
+ private static final int VALUE = 16;
+
+ private static final int CHAR = 1;
+ private static final int DELIMITER = 2;
+ private static final int STARTQUOTE = 4;
+ private static final int ENDQUOTE = 8;
+
+ private ResourceBuilder() {
+ }
+
+ public static ResourceImpl build(String uri, Map<String, String> headerMap) throws BundleException {
+ return build(new ResourceImpl(), uri, headerMap, false);
+ }
+
+ public static ResourceImpl build(String uri, Map<String, String> headerMap, boolean removeServiceRequirements) throws BundleException {
+ return build(new ResourceImpl(), uri, headerMap, removeServiceRequirements);
+ }
+
+ public static ResourceImpl build(ResourceImpl resource, String uri, Map<String, String> headerMap) throws BundleException {
+ return build(resource, uri, headerMap, false);
+ }
+
+ public static ResourceImpl build(ResourceImpl resource, String uri, Map<String, String> headerMap, boolean removeServiceRequirements) throws BundleException {
+ try {
+ return doBuild(resource, uri, headerMap, removeServiceRequirements);
+ } catch (Exception e) {
+ throw new BundleException("Unable to build resource for " + uri + ": " + e.getMessage(), e);
+ }
+ }
+
+ private static ResourceImpl doBuild(ResourceImpl resource, String uri, Map<String, String> headerMap, boolean removeServiceRequirements) throws BundleException {
+ // Verify that only manifest version 2 is specified.
+ String manifestVersion = getManifestVersion(headerMap);
+ if (manifestVersion == null || !manifestVersion.equals("2")) {
+ throw new BundleException("Unsupported 'Bundle-ManifestVersion' value: " + manifestVersion);
+ }
+
+ //
+ // Parse bundle version.
+ //
+
+ Version bundleVersion = Version.emptyVersion;
+ if (headerMap.get(Constants.BUNDLE_VERSION) != null) {
+ bundleVersion = VersionTable.getVersion(headerMap.get(Constants.BUNDLE_VERSION));
+ }
+
+ //
+ // Parse bundle symbolic name.
+ //
+
+ String bundleSymbolicName;
+ ParsedHeaderClause bundleCap = parseBundleSymbolicName(headerMap);
+ if (bundleCap == null) {
+ throw new BundleException("Bundle manifest must include bundle symbolic name");
+ }
+ bundleSymbolicName = (String) bundleCap.attrs.get(BundleRevision.BUNDLE_NAMESPACE);
+
+ // Now that we have symbolic name and version, create the resource
+ String type = headerMap.get(Constants.FRAGMENT_HOST) == null ? IdentityNamespace.TYPE_BUNDLE : IdentityNamespace.TYPE_FRAGMENT;
+ {
+ Map<String, String> dirs = new HashMap<>();
+ Map<String, Object> attrs = new HashMap<>();
+ attrs.put(IdentityNamespace.IDENTITY_NAMESPACE, bundleSymbolicName);
+ attrs.put(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE, type);
+ attrs.put(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, bundleVersion);
+ CapabilityImpl identity = new CapabilityImpl(resource, IdentityNamespace.IDENTITY_NAMESPACE, dirs, attrs);
+ resource.addCapability(identity);
+ }
+ if (uri != null) {
+ Map<String, Object> attrs = new HashMap<>();
+ attrs.put(ContentNamespace.CAPABILITY_URL_ATTRIBUTE, uri);
+ resource.addCapability(new CapabilityImpl(resource, ContentNamespace.CONTENT_NAMESPACE, Collections.<String, String>emptyMap(), attrs));
+ }
+
+ // Add a bundle and host capability to all
+ // non-fragment bundles. A host capability is the same
+ // as a require capability, but with a different capability
+ // namespace. Bundle capabilities resolve required-bundle
+ // dependencies, while host capabilities resolve fragment-host
+ // dependencies.
+ if (headerMap.get(Constants.FRAGMENT_HOST) == null) {
+ // All non-fragment bundles have bundle capability.
+ resource.addCapability(new CapabilityImpl(resource, BundleRevision.BUNDLE_NAMESPACE, bundleCap.dirs, bundleCap.attrs));
+ // A non-fragment bundle can choose to not have a host capability.
+ String attachment = bundleCap.dirs.get(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE);
+ attachment = (attachment == null) ? Constants.FRAGMENT_ATTACHMENT_RESOLVETIME : attachment;
+ if (!attachment.equalsIgnoreCase(Constants.FRAGMENT_ATTACHMENT_NEVER)) {
+ Map<String, Object> hostAttrs = new HashMap<>(bundleCap.attrs);
+ Object value = hostAttrs.remove(BundleRevision.BUNDLE_NAMESPACE);
+ hostAttrs.put(BundleRevision.HOST_NAMESPACE, value);
+ resource.addCapability(new CapabilityImpl(
+ resource, BundleRevision.HOST_NAMESPACE,
+ bundleCap.dirs,
+ hostAttrs));
+ }
+ }
+
+ //
+ // Parse Fragment-Host.
+ //
+
+ List<RequirementImpl> hostReqs = parseFragmentHost(resource, headerMap);
+
+ //
+ // Parse Require-Bundle
+ //
+
+ List<ParsedHeaderClause> rbClauses = parseStandardHeader(headerMap.get(Constants.REQUIRE_BUNDLE));
+ rbClauses = normalizeRequireClauses(rbClauses);
+ List<Requirement> rbReqs = convertRequires(rbClauses, resource);
+
+ //
+ // Parse Import-Package.
+ //
+
+ List<ParsedHeaderClause> importClauses = parseStandardHeader(headerMap.get(Constants.IMPORT_PACKAGE));
+ importClauses = normalizeImportClauses(importClauses);
+ List<Requirement> importReqs = convertImports(importClauses, resource);
+
+ //
+ // Parse DynamicImport-Package.
+ //
+
+ List<ParsedHeaderClause> dynamicClauses = parseStandardHeader(headerMap.get(Constants.DYNAMICIMPORT_PACKAGE));
+ dynamicClauses = normalizeDynamicImportClauses(dynamicClauses);
+ List<Requirement> dynamicReqs = convertImports(dynamicClauses, resource);
+
+ //
+ // Parse Require-Capability.
+ //
+
+ List<ParsedHeaderClause> requireClauses = parseStandardHeader(headerMap.get(Constants.REQUIRE_CAPABILITY));
+ requireClauses = normalizeRequireCapabilityClauses(requireClauses);
+ List<Requirement> requireReqs = convertRequireCapabilities(requireClauses, resource);
+
+ //
+ // Parse Bundle-RequiredExecutionEnvironment.
+ //
+ List<Requirement> breeReqs =
+ parseBreeHeader((String) headerMap.get(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT), resource);
+
+ //
+ // Parse Export-Package.
+ //
+
+ List<ParsedHeaderClause> exportClauses = parseStandardHeader(headerMap.get(Constants.EXPORT_PACKAGE));
+ exportClauses = normalizeExportClauses(exportClauses, bundleSymbolicName, bundleVersion);
+ List<Capability> exportCaps = convertExports(exportClauses, resource);
+
+ //
+ // Parse Provide-Capability.
+ //
+
+ List<ParsedHeaderClause> provideClauses = parseStandardHeader(headerMap.get(Constants.PROVIDE_CAPABILITY));
+ provideClauses = normalizeProvideCapabilityClauses(provideClauses);
+ List<Capability> provideCaps = convertProvideCapabilities(provideClauses, resource);
+
+ //
+ // Parse Import-Service and Export-Service
+ // if Require-Capability and Provide-Capability are not set for services
+ //
+
+ boolean hasServiceReferenceCapability = false;
+ for (Capability cap : exportCaps) {
+ hasServiceReferenceCapability |= ServiceNamespace.SERVICE_NAMESPACE.equals(cap.getNamespace());
+ }
+ if (!hasServiceReferenceCapability) {
+ List<ParsedHeaderClause> exportServices = parseStandardHeader(headerMap.get(Constants.EXPORT_SERVICE));
+ List<Capability> caps = convertExportService(exportServices, resource);
+ provideCaps.addAll(caps);
+ }
+
+ boolean hasServiceReferenceRequirement = false;
+ for (Requirement req : requireReqs) {
+ hasServiceReferenceRequirement |= ServiceNamespace.SERVICE_NAMESPACE.equals(req.getNamespace());
+ }
+ if (!hasServiceReferenceRequirement) {
+ List<ParsedHeaderClause> importServices = parseStandardHeader(headerMap.get(Constants.IMPORT_SERVICE));
+ List<Requirement> reqs = convertImportService(importServices, resource);
+ if (!reqs.isEmpty()) {
+ requireReqs.addAll(reqs);
+ hasServiceReferenceRequirement = true;
+ }
+ }
+
+ if (hasServiceReferenceRequirement && removeServiceRequirements) {
+ for (Iterator<Requirement> iterator = requireReqs.iterator(); iterator.hasNext();) {
+ Requirement req = iterator.next();
+ if (ServiceNamespace.SERVICE_NAMESPACE.equals(req.getNamespace())) {
+ iterator.remove();
+ }
+ }
+ }
+
+ // Combine all capabilities.
+ resource.addCapabilities(exportCaps);
+ resource.addCapabilities(provideCaps);
+
+ // Combine all requirements.
+ resource.addRequirements(hostReqs);
+ resource.addRequirements(importReqs);
+ resource.addRequirements(rbReqs);
+ resource.addRequirements(requireReqs);
+ resource.addRequirements(dynamicReqs);
+
+ return resource;
+ }
+
+ public static List<Requirement> parseRequirement(Resource resource, String requirement) throws BundleException {
+ List<ParsedHeaderClause> requireClauses = parseStandardHeader(requirement);
+ requireClauses = normalizeRequireCapabilityClauses(requireClauses);
+ return convertRequireCapabilities(requireClauses, resource);
+ }
+
+ public static List<Capability> parseCapability(Resource resource, String capability) throws BundleException {
+ List<ParsedHeaderClause> provideClauses = parseStandardHeader(capability);
+ provideClauses = normalizeProvideCapabilityClauses(provideClauses);
+ return convertProvideCapabilities(provideClauses, resource);
+ }
+
+ @SuppressWarnings("deprecation")
+ private static List<ParsedHeaderClause> normalizeImportClauses(List<ParsedHeaderClause> clauses) throws BundleException {
+ // Verify that the values are equals if the package specifies
+ // both version and specification-version attributes.
+ Set<String> dupeSet = new HashSet<>();
+ for (ParsedHeaderClause clause : clauses) {
+ // Check for "version" and "specification-version" attributes
+ // and verify they are the same if both are specified.
+ Object v = clause.attrs.get(Constants.VERSION_ATTRIBUTE);
+ Object sv = clause.attrs.get(Constants.PACKAGE_SPECIFICATION_VERSION);
+ if ((v != null) && (sv != null)) {
+ // Verify they are equal.
+ if (!((String) v).trim().equals(((String) sv).trim())) {
+ throw new IllegalArgumentException(
+ "Both version and specification-version are specified, but they are not equal.");
+ }
+ }
+
+ // Ensure that only the "version" attribute is used and convert
+ // it to the VersionRange type.
+ if ((v != null) || (sv != null)) {
+ clause.attrs.remove(Constants.PACKAGE_SPECIFICATION_VERSION);
+ v = (v == null) ? sv : v;
+ clause.attrs.put(Constants.VERSION_ATTRIBUTE, VersionRange.parseVersionRange(v.toString()));
+ }
+
+ // If bundle version is specified, then convert its type to VersionRange.
+ v = clause.attrs.get(Constants.BUNDLE_VERSION_ATTRIBUTE);
+ if (v != null) {
+ clause.attrs.put(Constants.BUNDLE_VERSION_ATTRIBUTE, VersionRange.parseVersionRange(v.toString()));
+ }
+
+ // Verify java.* is not imported, nor any duplicate imports.
+ for (String pkgName : clause.paths) {
+ if (!dupeSet.contains(pkgName)) {
+ // Verify that java.* packages are not imported.
+ if (pkgName.startsWith("java.")) {
+ throw new BundleException("Importing java.* packages not allowed: " + pkgName);
+ // The character "." has no meaning in the OSGi spec except
+ // when placed on the bundle class path. Some people, however,
+ // mistakenly think it means the default package when imported
+ // or exported. This is not correct. It is invalid.
+ } else if (pkgName.equals(".")) {
+ throw new BundleException("Importing '.' is invalid.");
+ // Make sure a package name was specified.
+ } else if (pkgName.length() == 0) {
+ throw new BundleException(
+ "Imported package names cannot be zero length.");
+ }
+ dupeSet.add(pkgName);
+ } else {
+ throw new BundleException("Duplicate import: " + pkgName);
+ }
+ }
+ }
+
+ return clauses;
+ }
+
+ private static List<Capability> convertExportService(List<ParsedHeaderClause> clauses, Resource resource) {
+ List<Capability> capList = new ArrayList<>();
+ for (ParsedHeaderClause clause : clauses) {
+ for (String path : clause.paths) {
+ Map<String, String> dirs = new LinkedHashMap<>();
+ dirs.put(ServiceNamespace.CAPABILITY_EFFECTIVE_DIRECTIVE, ServiceNamespace.EFFECTIVE_ACTIVE);
+ Map<String, Object> attrs = new LinkedHashMap<>();
+ attrs.put(Constants.OBJECTCLASS, path);
+ attrs.putAll(clause.attrs);
+ capList.add(new CapabilityImpl(
+ resource,
+ ServiceNamespace.SERVICE_NAMESPACE,
+ dirs,
+ attrs));
+ }
+ }
+ return capList;
+ }
+
+ private static List<Requirement> convertImportService(List<ParsedHeaderClause> clauses, Resource resource) throws BundleException {
+ try {
+ List<Requirement> reqList = new ArrayList<>();
+ for (ParsedHeaderClause clause : clauses) {
+ for (String path : clause.paths) {
+ String multiple = clause.dirs.get("multiple");
+ String avail = clause.dirs.get("availability");
+ String filter = (String) clause.attrs.get("filter");
+ Map<String, String> dirs = new LinkedHashMap<>();
+ dirs.put(ServiceNamespace.REQUIREMENT_EFFECTIVE_DIRECTIVE, ServiceNamespace.EFFECTIVE_ACTIVE);
+ if ("optional".equals(avail)) {
+ dirs.put(ServiceNamespace.REQUIREMENT_RESOLUTION_DIRECTIVE, ServiceNamespace.RESOLUTION_OPTIONAL);
+ }
+ if ("true".equals(multiple)) {
+ dirs.put(ServiceNamespace.REQUIREMENT_CARDINALITY_DIRECTIVE, ServiceNamespace.CARDINALITY_MULTIPLE);
+ }
+ if (filter == null) {
+ filter = "(" + Constants.OBJECTCLASS + "=" + path + ")";
+ } else if (!filter.startsWith("(") && !filter.endsWith(")")) {
+ filter = "(&(" + Constants.OBJECTCLASS + "=" + path + ")(" + filter + "))";
+ } else {
+ filter = "(&(" + Constants.OBJECTCLASS + "=" + path + ")" + filter + ")";
+ }
+ dirs.put(ServiceNamespace.REQUIREMENT_FILTER_DIRECTIVE, filter);
+ reqList.add(new RequirementImpl(
+ resource,
+ ServiceNamespace.SERVICE_NAMESPACE,
+ dirs,
+ Collections.<String, Object>emptyMap(),
+ SimpleFilter.parse(filter)));
+ }
+ }
+ return reqList;
+ } catch (Exception ex) {
+ throw new BundleException("Error creating requirement: " + ex, ex);
+ }
+ }
+
+ private static List<Requirement> convertImports(List<ParsedHeaderClause> clauses, Resource resource) {
+ // Now convert generic header clauses into requirements.
+ List<Requirement> reqList = new ArrayList<>();
+ for (ParsedHeaderClause clause : clauses) {
+ for (String path : clause.paths) {
+ // Prepend the package name to the array of attributes.
+ Map<String, Object> attrs = clause.attrs;
+ // Note that we use a linked hash map here to ensure the
+ // package attribute is first, which will make indexing
+ // more efficient.
+ // TODO: OSGi R4.3 - This is ordering is kind of hacky.
+ // Prepend the package name to the array of attributes.
+ Map<String, Object> newAttrs = new LinkedHashMap<>(attrs.size() + 1);
+ // We want this first from an indexing perspective.
+ newAttrs.put(BundleRevision.PACKAGE_NAMESPACE, path);
+ newAttrs.putAll(attrs);
+ // But we need to put it again to make sure it wasn't overwritten.
+ newAttrs.put(BundleRevision.PACKAGE_NAMESPACE, path);
+
+ // Create filter now so we can inject filter directive.
+ SimpleFilter sf = SimpleFilter.convert(newAttrs);
+
+ // Inject filter directive.
+ // TODO: OSGi R4.3 - Can we insert this on demand somehow?
+ Map<String, String> dirs = clause.dirs;
+ Map<String, String> newDirs = new HashMap<>(dirs.size() + 1);
+ newDirs.putAll(dirs);
+ newDirs.put(Constants.FILTER_DIRECTIVE, sf.toString());
+
+ // Create package requirement and add to requirement list.
+ reqList.add(
+ new RequirementImpl(
+ resource,
+ BundleRevision.PACKAGE_NAMESPACE,
+ newDirs,
+ Collections.<String, Object>emptyMap(),
+ sf)
+ );
+ }
+ }
+
+ return reqList;
+ }
+
+ @SuppressWarnings("deprecation")
+ private static List<ParsedHeaderClause> normalizeDynamicImportClauses(List<ParsedHeaderClause> clauses) throws BundleException {
+ // Verify that the values are equals if the package specifies
+ // both version and specification-version attributes.
+ for (ParsedHeaderClause clause : clauses) {
+ // Add the resolution directive to indicate that these are
+ // dynamic imports.
+ clause.dirs.put(Constants.RESOLUTION_DIRECTIVE, RESOLUTION_DYNAMIC);
+
+ // Check for "version" and "specification-version" attributes
+ // and verify they are the same if both are specified.
+ Object v = clause.attrs.get(Constants.VERSION_ATTRIBUTE);
+ Object sv = clause.attrs.get(Constants.PACKAGE_SPECIFICATION_VERSION);
+ if ((v != null) && (sv != null)) {
+ // Verify they are equal.
+ if (!((String) v).trim().equals(((String) sv).trim())) {
+ throw new IllegalArgumentException(
+ "Both version and specification-version are specified, but they are not equal.");
+ }
+ }
+
+ // Ensure that only the "version" attribute is used and convert
+ // it to the VersionRange type.
+ if ((v != null) || (sv != null)) {
+ clause.attrs.remove(Constants.PACKAGE_SPECIFICATION_VERSION);
+ v = (v == null) ? sv : v;
+ clause.attrs.put(Constants.VERSION_ATTRIBUTE, VersionRange.parseVersionRange(v.toString()));
+ }
+
+ // If bundle version is specified, then convert its type to VersionRange.
+ v = clause.attrs.get(Constants.BUNDLE_VERSION_ATTRIBUTE);
+ if (v != null) {
+ clause.attrs.put(Constants.BUNDLE_VERSION_ATTRIBUTE, VersionRange.parseVersionRange(v.toString()));
+ }
+
+ // Dynamic imports can have duplicates, so verify that java.*
+ // packages are not imported.
+ for (String pkgName : clause.paths) {
+ if (pkgName.startsWith("java.")) {
+ throw new BundleException("Dynamically importing java.* packages not allowed: " + pkgName);
+ } else if (!pkgName.equals("*") && pkgName.endsWith("*") && !pkgName.endsWith(".*")) {
+ throw new BundleException("Partial package name wild carding is not allowed: " + pkgName);
+ }
+ }
+ }
+
+ return clauses;
+ }
+
+ private static List<ParsedHeaderClause> normalizeRequireCapabilityClauses(
+ List<ParsedHeaderClause> clauses) throws BundleException {
+
+ // Convert attributes into specified types.
+ for (ParsedHeaderClause clause : clauses) {
+ for (Map.Entry<String, Object> entry : clause.attrs.entrySet()) {
+ if (entry.getKey().equals("version")) {
+ clause.attrs.put(entry.getKey(), new VersionRange(entry.getValue().toString()));
+ }
+ }
+ for (Map.Entry<String, String> entry : clause.types.entrySet()) {
+ String type = entry.getValue();
+ if (!type.equals("String")) {
+ if (type.equals("Double")) {
+ clause.attrs.put(
+ entry.getKey(),
+ new Double(clause.attrs.get(entry.getKey()).toString().trim()));
+ } else if (type.equals("Version")) {
+ clause.attrs.put(
+ entry.getKey(),
+ new Version(clause.attrs.get(entry.getKey()).toString().trim()));
+ } else if (type.equals("Long")) {
+ clause.attrs.put(
+ entry.getKey(),
+ new Long(clause.attrs.get(entry.getKey()).toString().trim()));
+ } else if (type.startsWith("List")) {
+ int startIdx = type.indexOf('<');
+ int endIdx = type.indexOf('>');
+ if (((startIdx > 0) && (endIdx <= startIdx))
+ || ((startIdx < 0) && (endIdx > 0))) {
+ throw new BundleException(
+ "Invalid Provide-Capability attribute list type for '"
+ + entry.getKey()
+ + "' : "
+ + type
+ );
+ }
+
+ String listType = "String";
+ if (endIdx > startIdx) {
+ listType = type.substring(startIdx + 1, endIdx).trim();
+ }
+
+ List<String> tokens = parseDelimitedString(
+ clause.attrs.get(entry.getKey()).toString(), ",", false);
+ List<Object> values = new ArrayList<>(tokens.size());
+ for (String token : tokens) {
+ switch (listType) {
+ case "String":
+ values.add(token);
+ break;
+ case "Double":
+ values.add(new Double(token.trim()));
+ break;
+ case "Version":
+ values.add(new Version(token.trim()));
+ break;
+ case "Long":
+ values.add(new Long(token.trim()));
+ break;
+ default:
+ throw new BundleException(
+ "Unknown Provide-Capability attribute list type for '"
+ + entry.getKey()
+ + "' : "
+ + type
+ );
+ }
+ }
+ clause.attrs.put(
+ entry.getKey(),
+ values);
+ } else {
+ throw new BundleException(
+ "Unknown Provide-Capability attribute type for '"
+ + entry.getKey()
+ + "' : "
+ + type
+ );
+ }
+ }
+ }
+ }
+
+ return clauses;
+ }
+
+ private static List<ParsedHeaderClause> normalizeProvideCapabilityClauses(
+ List<ParsedHeaderClause> clauses) throws BundleException {
+
+ // Convert attributes into specified types.
+ for (ParsedHeaderClause clause : clauses) {
+ for (Map.Entry<String, String> entry : clause.types.entrySet()) {
+ String type = entry.getValue();
+ if (!type.equals("String")) {
+ if (type.equals("Double")) {
+ clause.attrs.put(
+ entry.getKey(),
+ new Double(clause.attrs.get(entry.getKey()).toString().trim()));
+ } else if (type.equals("Version")) {
+ clause.attrs.put(
+ entry.getKey(),
+ new Version(clause.attrs.get(entry.getKey()).toString().trim()));
+ } else if (type.equals("Long")) {
+ clause.attrs.put(
+ entry.getKey(),
+ new Long(clause.attrs.get(entry.getKey()).toString().trim()));
+ } else if (type.startsWith("List")) {
+ int startIdx = type.indexOf('<');
+ int endIdx = type.indexOf('>');
+ if (((startIdx > 0) && (endIdx <= startIdx))
+ || ((startIdx < 0) && (endIdx > 0))) {
+ throw new BundleException(
+ "Invalid Provide-Capability attribute list type for '"
+ + entry.getKey()
+ + "' : "
+ + type
+ );
+ }
+
+ String listType = "String";
+ if (endIdx > startIdx) {
+ listType = type.substring(startIdx + 1, endIdx).trim();
+ }
+
+ List<String> tokens = parseDelimitedString(
+ clause.attrs.get(entry.getKey()).toString(), ",", false);
+ List<Object> values = new ArrayList<>(tokens.size());
+ for (String token : tokens) {
+ switch (listType) {
+ case "String":
+ values.add(token);
+ break;
+ case "Double":
+ values.add(new Double(token.trim()));
+ break;
+ case "Version":
+ values.add(new Version(token.trim()));
+ break;
+ case "Long":
+ values.add(new Long(token.trim()));
+ break;
+ default:
+ throw new BundleException(
+ "Unknown Provide-Capability attribute list type for '"
+ + entry.getKey()
+ + "' : "
+ + type
+ );
+ }
+ }
+ clause.attrs.put(
+ entry.getKey(),
+ values);
+ } else {
+ throw new BundleException(
+ "Unknown Provide-Capability attribute type for '"
+ + entry.getKey()
+ + "' : "
+ + type
+ );
+ }
+ }
+ }
+ }
+
+ return clauses;
+ }
+
+ private static List<Requirement> convertRequireCapabilities(
+ List<ParsedHeaderClause> clauses, Resource resource) throws BundleException {
+
+ // Now convert generic header clauses into requirements.
+ List<Requirement> reqList = new ArrayList<>();
+ for (ParsedHeaderClause clause : clauses) {
+ try {
+ String filterStr = clause.dirs.get(Constants.FILTER_DIRECTIVE);
+ SimpleFilter sf = (filterStr != null)
+ ? SimpleFilter.parse(filterStr)
+ : SimpleFilter.convert(clause.attrs);
+ for (String path : clause.paths) {
+ // Create requirement and add to requirement list.
+ reqList.add(new RequirementImpl(
+ resource, path, clause.dirs, clause.attrs, sf));
+ }
+ } catch (Exception ex) {
+ throw new BundleException("Error creating requirement: " + ex, ex);
+ }
+ }
+
+ return reqList;
+ }
+
+ private static List<Capability> convertProvideCapabilities(
+ List<ParsedHeaderClause> clauses, Resource resource) throws BundleException {
+
+ List<Capability> capList = new ArrayList<>();
+ for (ParsedHeaderClause clause : clauses) {
+ for (String path : clause.paths) {
+ if (path.startsWith("osgi.wiring.")) {
+// throw new BundleException("Manifest cannot use Provide-Capability for '" + path + "' namespace.");
+ }
+
+ // Create package capability and add to capability list.
+ capList.add(new CapabilityImpl(resource, path, clause.dirs, clause.attrs));
+ }
+ }
+
... 9653 lines suppressed ...