You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by gn...@apache.org on 2014/04/23 15:49:17 UTC
[2/4] git commit: [KARAF-2930] Refactory repository support and fully
support for the xml format defined by the OSGi Repository specification
[KARAF-2930] Refactory repository support and fully support for the xml format defined by the OSGi Repository specification
Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/3574b440
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/3574b440
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/3574b440
Branch: refs/heads/master
Commit: 3574b440f45093802507e5fdd427289a5b4e9984
Parents: 7456445
Author: Guillaume Nodet <gn...@gmail.com>
Authored: Wed Apr 23 11:30:45 2014 +0200
Committer: Guillaume Nodet <gn...@gmail.com>
Committed: Wed Apr 23 15:48:42 2014 +0200
----------------------------------------------------------------------
.../region/SubsystemResolveContext.java | 4 +-
.../internal/repository/BaseRepository.java | 7 +
.../internal/repository/CacheRepository.java | 59 ----
.../repository/HttpMetadataProvider.java | 88 ------
.../internal/repository/JsonRepository.java | 117 ++++++++
.../internal/repository/MetadataProvider.java | 29 --
.../internal/repository/MetadataRepository.java | 43 ---
.../internal/repository/StaticRepository.java | 33 ---
.../internal/repository/StaxParser.java | 289 +++++++++++++++++++
.../features/internal/repository/UrlLoader.java | 88 ++++++
.../internal/repository/XmlRepository.java | 163 +++++++++++
.../internal/resolver/ResourceImpl.java | 9 +
.../internal/repository/RepositoryTest.java | 58 ++++
.../features/internal/repository/repo.json | 9 +
.../karaf/features/internal/repository/repo.xml | 53 ++++
pom.xml | 2 +
16 files changed, 797 insertions(+), 254 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/karaf/blob/3574b440/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolveContext.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolveContext.java b/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolveContext.java
index d5a3113..949e37a 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolveContext.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolveContext.java
@@ -25,7 +25,7 @@ import java.util.List;
import java.util.Map;
import org.apache.felix.resolver.Util;
-import org.apache.karaf.features.internal.repository.StaticRepository;
+import org.apache.karaf.features.internal.repository.BaseRepository;
import org.eclipse.equinox.region.Region;
import org.eclipse.equinox.region.RegionDigraph;
import org.eclipse.equinox.region.RegionFilter;
@@ -61,7 +61,7 @@ public class SubsystemResolveContext extends ResolveContext {
this.digraph = digraph;
prepare(root);
- repository = new StaticRepository(resToSub.keySet());
+ repository = new BaseRepository(resToSub.keySet());
}
void prepare(Subsystem subsystem) {
http://git-wip-us.apache.org/repos/asf/karaf/blob/3574b440/features/core/src/main/java/org/apache/karaf/features/internal/repository/BaseRepository.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/repository/BaseRepository.java b/features/core/src/main/java/org/apache/karaf/features/internal/repository/BaseRepository.java
index c4c0d16..e4befcb 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/repository/BaseRepository.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/repository/BaseRepository.java
@@ -44,6 +44,13 @@ public class BaseRepository implements Repository {
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();
http://git-wip-us.apache.org/repos/asf/karaf/blob/3574b440/features/core/src/main/java/org/apache/karaf/features/internal/repository/CacheRepository.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/repository/CacheRepository.java b/features/core/src/main/java/org/apache/karaf/features/internal/repository/CacheRepository.java
deleted file mode 100644
index 7916821..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/internal/repository/CacheRepository.java
+++ /dev/null
@@ -1,59 +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.features.internal.repository;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.osgi.resource.Capability;
-import org.osgi.resource.Requirement;
-import org.osgi.service.repository.Repository;
-
-public class CacheRepository implements Repository {
-
- private final Repository repository;
- private final Map<Requirement, Collection<Capability>> cache =
- new ConcurrentHashMap<Requirement, Collection<Capability>>();
-
- public CacheRepository(Repository repository) {
- this.repository = repository;
- }
-
- @Override
- public Map<Requirement, Collection<Capability>> findProviders(Collection<? extends Requirement> requirements) {
- List<Requirement> missing = new ArrayList<Requirement>();
- Map<Requirement, Collection<Capability>> result = new HashMap<Requirement, Collection<Capability>>();
- for (Requirement requirement : requirements) {
- Collection<Capability> caps = cache.get(requirement);
- if (caps == null) {
- missing.add(requirement);
- } else {
- result.put(requirement, caps);
- }
- }
- Map<Requirement, Collection<Capability>> newCache = repository.findProviders(missing);
- for (Requirement requirement : newCache.keySet()) {
- cache.put(requirement, newCache.get(requirement));
- result.put(requirement, newCache.get(requirement));
- }
- return result;
- }
-}
http://git-wip-us.apache.org/repos/asf/karaf/blob/3574b440/features/core/src/main/java/org/apache/karaf/features/internal/repository/HttpMetadataProvider.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/repository/HttpMetadataProvider.java b/features/core/src/main/java/org/apache/karaf/features/internal/repository/HttpMetadataProvider.java
deleted file mode 100644
index 1aecef1..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/internal/repository/HttpMetadataProvider.java
+++ /dev/null
@@ -1,88 +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.features.internal.repository;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.Map;
-import java.util.zip.GZIPInputStream;
-
-import org.apache.karaf.features.internal.util.JsonReader;
-
-/**
- */
-public class HttpMetadataProvider implements MetadataProvider {
-
- public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";
- public static final String HEADER_CONTENT_ENCODING = "Content-Encoding";
- public static final String GZIP = "gzip";
-
- private final String url;
- private long lastModified;
- private Map<String, Map<String, String>> metadatas;
-
- public HttpMetadataProvider(String url) {
- this.url = url;
- }
-
- @Override
- public long getLastModified() {
- return lastModified;
- }
-
- @Override
- public Map<String, Map<String, String>> getMetadatas() {
- try {
- HttpURLConnection.setFollowRedirects(false);
- HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection();
- if (lastModified > 0) {
- con.setIfModifiedSince(lastModified);
- }
- con.setRequestProperty(HEADER_ACCEPT_ENCODING, GZIP);
- if (con.getResponseCode() == HttpURLConnection.HTTP_OK) {
- lastModified = con.getLastModified();
- InputStream is = con.getInputStream();
- if (GZIP.equals(con.getHeaderField(HEADER_CONTENT_ENCODING))) {
- is = new GZIPInputStream(is);
- }
- metadatas = verify(JsonReader.read(is));
- } else if (con.getResponseCode() != HttpURLConnection.HTTP_NOT_MODIFIED) {
- throw new IOException("Unexpected http response: "
- + con.getResponseCode() + " " + con.getResponseMessage());
- }
- return metadatas;
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- private Map<String, Map<String, String>> verify(Object value) {
- Map<?,?> obj = Map.class.cast(value);
- for (Map.Entry<?,?> entry : obj.entrySet()) {
- String.class.cast(entry.getKey());
- Map<?,?> child = Map.class.cast(entry.getValue());
- for (Map.Entry<?,?> ce : child.entrySet()) {
- String.class.cast(ce.getKey());
- String.class.cast(ce.getValue());
- }
- }
- return (Map<String, Map<String, String>>) obj;
- }
-
-}
http://git-wip-us.apache.org/repos/asf/karaf/blob/3574b440/features/core/src/main/java/org/apache/karaf/features/internal/repository/JsonRepository.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/repository/JsonRepository.java b/features/core/src/main/java/org/apache/karaf/features/internal/repository/JsonRepository.java
new file mode 100644
index 0000000..f1f3c0b
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/repository/JsonRepository.java
@@ -0,0 +1,117 @@
+/*
+ * 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.features.internal.repository;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.apache.karaf.features.internal.resolver.ResourceBuilder;
+import org.apache.karaf.features.internal.util.JsonReader;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Repository using a JSON representation of resource metadata.
+ * The json should be a map: the key is the resource uri and the
+ * value is a map of resource headers.
+ * The content of the URL can be gzipped.
+ */
+public class JsonRepository extends BaseRepository {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(JsonRepository.class);
+
+ private final UrlLoader loader;
+ private final ReadWriteLock lock = new ReentrantReadWriteLock();
+
+ public JsonRepository(String url) {
+ loader = new UrlLoader(url) {
+ @Override
+ protected boolean doRead(InputStream is) throws IOException {
+ return JsonRepository.this.doRead(is);
+ }
+ };
+ }
+
+ @Override
+ public List<Resource> getResources() {
+ checkAndLoadCache();
+ lock.readLock().lock();
+ try {
+ return super.getResources();
+ } finally {
+ lock.readLock().unlock();
+ }
+ }
+
+ @Override
+ public Map<Requirement, Collection<Capability>> findProviders(Collection<? extends Requirement> requirements) {
+ checkAndLoadCache();
+ lock.readLock().lock();
+ try {
+ return super.findProviders(requirements);
+ } finally {
+ lock.readLock().unlock();
+ }
+ }
+
+ private void checkAndLoadCache() {
+ loader.checkAndLoadCache();
+ }
+
+ protected boolean doRead(InputStream is) throws IOException {
+ Map<String, Map<String, String>> metadatas = verify(JsonReader.read(is));
+ lock.writeLock().lock();
+ try {
+ resources.clear();
+ capSets.clear();
+ for (Map.Entry<String, Map<String, String>> metadata : metadatas.entrySet()) {
+ try {
+ Resource resource = ResourceBuilder.build(metadata.getKey(), metadata.getValue());
+ addResource(resource);
+ } catch (Exception e) {
+ LOGGER.info("Unable to build resource for " + metadata.getKey(), e);
+ }
+ }
+ return true;
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private Map<String, Map<String, String>> verify(Object value) {
+ Map<?,?> obj = Map.class.cast(value);
+ for (Map.Entry<?,?> entry : obj.entrySet()) {
+ String.class.cast(entry.getKey());
+ Map<?,?> child = Map.class.cast(entry.getValue());
+ for (Map.Entry<?,?> ce : child.entrySet()) {
+ String.class.cast(ce.getKey());
+ String.class.cast(ce.getValue());
+ }
+ }
+ return (Map<String, Map<String, String>>) obj;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/3574b440/features/core/src/main/java/org/apache/karaf/features/internal/repository/MetadataProvider.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/repository/MetadataProvider.java b/features/core/src/main/java/org/apache/karaf/features/internal/repository/MetadataProvider.java
deleted file mode 100644
index 9ac54a1..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/internal/repository/MetadataProvider.java
+++ /dev/null
@@ -1,29 +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.features.internal.repository;
-
-import java.util.Map;
-
-/**
- */
-public interface MetadataProvider {
-
- long getLastModified();
-
- Map<String, Map<String, String>> getMetadatas();
-
-}
http://git-wip-us.apache.org/repos/asf/karaf/blob/3574b440/features/core/src/main/java/org/apache/karaf/features/internal/repository/MetadataRepository.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/repository/MetadataRepository.java b/features/core/src/main/java/org/apache/karaf/features/internal/repository/MetadataRepository.java
deleted file mode 100644
index 2d4fbba..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/internal/repository/MetadataRepository.java
+++ /dev/null
@@ -1,43 +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.features.internal.repository;
-
-import java.util.Map;
-
-import org.apache.karaf.features.internal.resolver.ResourceBuilder;
-import org.osgi.resource.Resource;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- */
-public class MetadataRepository extends BaseRepository {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(MetadataRepository.class);
-
- public MetadataRepository(MetadataProvider provider) {
- Map<String, Map<String, String>> metadatas = provider.getMetadatas();
- for (Map.Entry<String, Map<String, String>> metadata : metadatas.entrySet()) {
- try {
- Resource resource = ResourceBuilder.build(metadata.getKey(), metadata.getValue());
- addResource(resource);
- } catch (Exception e) {
- LOGGER.info("Unable to build resource for " + metadata.getKey(), e);
- }
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/karaf/blob/3574b440/features/core/src/main/java/org/apache/karaf/features/internal/repository/StaticRepository.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/repository/StaticRepository.java b/features/core/src/main/java/org/apache/karaf/features/internal/repository/StaticRepository.java
deleted file mode 100644
index f289c8d..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/internal/repository/StaticRepository.java
+++ /dev/null
@@ -1,33 +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.features.internal.repository;
-
-import java.util.Collection;
-
-import org.osgi.resource.Resource;
-
-/**
- */
-public class StaticRepository extends BaseRepository {
-
- public StaticRepository(Collection<Resource> resources) {
- for (Resource resource : resources) {
- addResource(resource);
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/karaf/blob/3574b440/features/core/src/main/java/org/apache/karaf/features/internal/repository/StaxParser.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/repository/StaxParser.java b/features/core/src/main/java/org/apache/karaf/features/internal/repository/StaxParser.java
new file mode 100644
index 0000000..9e13e8e
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/repository/StaxParser.java
@@ -0,0 +1,289 @@
+/*
+ * 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.features.internal.repository;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.stream.Location;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
+import org.apache.karaf.features.internal.resolver.CapabilityImpl;
+import org.apache.karaf.features.internal.resolver.RequirementImpl;
+import org.apache.karaf.features.internal.resolver.ResourceImpl;
+import org.osgi.framework.Version;
+import org.osgi.resource.Resource;
+
+import static javax.xml.stream.XMLStreamConstants.CHARACTERS;
+import static javax.xml.stream.XMLStreamConstants.END_ELEMENT;
+import static javax.xml.stream.XMLStreamConstants.START_ELEMENT;
+
+/**
+ * Repository XML xml based on StaX
+ */
+public class StaxParser {
+
+ public static final String REPOSITORY = "repository";
+ public static final String REPO_NAME = "name";
+ public static final String INCREMENT = "increment";
+ public static final String REFERRAL = "referral";
+ public static final String DEPTH = "depth";
+ public static final String URL = "url";
+ public static final String RESOURCE = "resource";
+ public static final String CAPABILITY = "capability";
+ public static final String REQUIREMENT = "requirement";
+ public static final String NAMESPACE = "namespace";
+ public static final String ATTRIBUTE = "attribute";
+ public static final String DIRECTIVE = "directive";
+ public static final String NAME = "name";
+ public static final String VALUE = "value";
+ public static final String TYPE = "type";
+
+ public static class Referral {
+ String url;
+ int depth = Integer.MAX_VALUE;
+ }
+
+ public static class XmlRepository {
+ String name;
+ long increment;
+ List<Referral> referrals = new ArrayList<Referral>();
+ List<Resource> resources = new ArrayList<Resource>();
+ }
+
+ public static XmlRepository parse(InputStream is) throws XMLStreamException {
+ return parse(is, null);
+ }
+
+ public static XmlRepository parse(InputStream is, XmlRepository previous) throws XMLStreamException {
+ XMLStreamReader reader = getFactory().createXMLStreamReader(is);
+ int event = reader.nextTag();
+ if (event != START_ELEMENT || !REPOSITORY.equals(reader.getLocalName())) {
+ throw new IllegalStateException("Expected element 'repository' at the root of the document");
+ }
+ XmlRepository repo = new XmlRepository();
+ for (int i = 0, nb = reader.getAttributeCount(); i < nb; i++) {
+ String attrName = reader.getAttributeLocalName(i);
+ String attrValue = reader.getAttributeValue(i);
+ if (REPO_NAME.equals(attrName)) {
+ repo.name = attrName;
+ } else if (INCREMENT.equals(attrName)) {
+ repo.increment = Integer.parseInt(attrValue);
+ } else {
+ throw new IllegalStateException("Unexpected attribute '" + attrName + "'");
+ }
+ }
+ if (previous != null && repo.increment == previous.increment) {
+ return previous;
+ }
+ while ((event = reader.nextTag()) == START_ELEMENT) {
+ String element = reader.getLocalName();
+ if (REFERRAL.equals(element)) {
+ Referral referral = new Referral();
+ for (int i = 0, nb = reader.getAttributeCount(); i < nb; i++) {
+ String attrName = reader.getAttributeLocalName(i);
+ String attrValue = reader.getAttributeValue(i);
+ if (DEPTH.equals(attrName)) {
+ referral.depth = Integer.parseInt(attrValue);
+ } else if (URL.equals(attrName)) {
+ referral.url = attrValue;
+ } else {
+ throw new IllegalStateException("Unexpected attribute '" + attrName + "'");
+ }
+ }
+ if (referral.url == null) {
+ throw new IllegalStateException("Expected attribute '" + URL + "'");
+ }
+ repo.referrals.add(referral);
+ sanityCheckEndElement(reader, reader.nextTag(), REFERRAL);
+ } else if (RESOURCE.equals(element)) {
+ repo.resources.add(parseResource(reader));
+ } else {
+ throw new IllegalStateException("Unsupported element '" + element + "'. Expected 'referral' or 'resource'");
+ }
+ }
+ // Sanity check
+ sanityCheckEndElement(reader, event, REPOSITORY);
+ return repo;
+ }
+
+ private static void sanityCheckEndElement(XMLStreamReader reader, int event, String element) {
+ if (event != END_ELEMENT || !element.equals(reader.getLocalName())) {
+ throw new IllegalStateException("Unexpected state while finishing element " + element);
+ }
+ }
+
+ private static ResourceImpl parseResource(XMLStreamReader reader) {
+ try {
+ if (reader.getAttributeCount() > 0) {
+ throw new IllegalStateException("Unexpected attribute '" + reader.getAttributeLocalName(0) + "'");
+ }
+ ResourceImpl resource = new ResourceImpl();
+ int event;
+ while ((event = reader.nextTag()) == START_ELEMENT) {
+ String element = reader.getLocalName();
+ if (CAPABILITY.equals(element)) {
+ resource.addCapability(parseCapability(reader, resource));
+ } else if (REQUIREMENT.equals(element)) {
+ resource.addRequirement(parseRequirement(reader, resource));
+ } else {
+ while ((event = reader.next()) != END_ELEMENT) {
+ switch (event) {
+ case START_ELEMENT:
+ throw new IllegalStateException("Unexpected element '" + reader.getLocalName() + "' inside 'resource' element");
+ case CHARACTERS:
+ throw new IllegalStateException("Unexpected text inside 'resource' element");
+ }
+ }
+ }
+ }
+ // Sanity check
+ sanityCheckEndElement(reader, event, RESOURCE);
+ return resource;
+ } catch (Exception e) {
+ Location loc = reader.getLocation();
+ if (loc != null) {
+ throw new IllegalStateException("Error while parsing resource at line " + loc.getLineNumber() + " and column " + loc.getColumnNumber(), e);
+ } else {
+ throw new IllegalStateException("Error while parsing resource", e);
+ }
+ }
+ }
+
+ private static CapabilityImpl parseCapability(XMLStreamReader reader, ResourceImpl resource) throws XMLStreamException {
+ String[] namespace = new String[1];
+ Map<String, String> directives = new HashMap<String, String>();
+ Map<String, Object> attributes = new HashMap<String, Object>();
+ parseClause(reader, namespace, directives, attributes);
+ sanityCheckEndElement(reader, reader.getEventType(), CAPABILITY);
+ return new CapabilityImpl(resource, namespace[0], directives, attributes);
+ }
+
+ private static RequirementImpl parseRequirement(XMLStreamReader reader, ResourceImpl resource) throws XMLStreamException {
+ String[] namespace = new String[1];
+ Map<String, String> directives = new HashMap<String, String>();
+ Map<String, Object> attributes = new HashMap<String, Object>();
+ parseClause(reader, namespace, directives, attributes);
+ sanityCheckEndElement(reader, reader.getEventType(), REQUIREMENT);
+ return new RequirementImpl(resource, namespace[0], directives, attributes);
+ }
+
+ private static void parseClause(XMLStreamReader reader, String[] namespace, Map<String, String> directives, Map<String, Object> attributes) throws XMLStreamException {
+ namespace[0] = null;
+ for (int i = 0, nb = reader.getAttributeCount(); i < nb; i++) {
+ String name = reader.getAttributeLocalName(i);
+ String value = reader.getAttributeValue(i);
+ if (NAMESPACE.equals(name)) {
+ namespace[0] = value;
+ } else {
+ throw new IllegalStateException("Unexpected attribute: '" + name + "'. Expected 'namespace'");
+ }
+ }
+ if (namespace[0] == null) {
+ throw new IllegalStateException("Expected attribute 'namespace'");
+ }
+ while (reader.nextTag() == START_ELEMENT) {
+ String element = reader.getLocalName();
+ if (DIRECTIVE.equals(element)) {
+ String name = null;
+ String value = null;
+ for (int i = 0, nb = reader.getAttributeCount(); i < nb; i++) {
+ String attName = reader.getAttributeLocalName(i);
+ String attValue = reader.getAttributeValue(i);
+ if (NAME.equals(attName)) {
+ name = attValue;
+ } else if (VALUE.equals(attName)) {
+ value = attValue;
+ } else {
+ throw new IllegalStateException("Unexpected attribute: '" + attName + "'. Expected 'name', or 'value'.");
+ }
+ }
+ if (name == null || value == null) {
+ throw new IllegalStateException("Expected attribute 'name' and 'value'");
+ }
+ directives.put(name, value);
+ sanityCheckEndElement(reader, reader.nextTag(), DIRECTIVE);
+ } else if (ATTRIBUTE.equals(element)) {
+ String name = null;
+ String value = null;
+ String type = "String";
+ for (int i = 0, nb = reader.getAttributeCount(); i < nb; i++) {
+ String attName = reader.getAttributeLocalName(i);
+ String attValue = reader.getAttributeValue(i);
+ if (NAME.equals(attName)) {
+ name = attValue;
+ } else if (VALUE.equals(attName)) {
+ value = attValue;
+ } else if (TYPE.equals(attName)) {
+ type = attValue;
+ } else {
+ throw new IllegalStateException("Unexpected attribute: '" + attName + "'. Expected 'name', 'value' or 'type'.");
+ }
+ }
+ if (name == null || value == null) {
+ throw new IllegalStateException("Expected attribute 'name' and 'value'");
+ }
+ attributes.put(name, parseAttribute(value, type));
+ sanityCheckEndElement(reader, reader.nextTag(), ATTRIBUTE);
+ } else {
+ throw new IllegalStateException("Unexpected element: '" + element + ". Expected 'directive' or 'attribute'");
+ }
+ }
+ }
+
+ private static Object parseAttribute(String value, String type) {
+ if ("String".equals(type)) {
+ return value;
+ } else if ("Version".equals(type)) {
+ return Version.parseVersion(value);
+ } else if ("Long".equals(type)) {
+ return Long.parseLong(value.trim());
+ } else if ("Double".equals(type)) {
+ return Double.parseDouble(value.trim());
+ } else if (type.startsWith("List<") && type.endsWith(">")) {
+ type = type.substring("List<".length(), type.length() - 1);
+ List<Object> list = new ArrayList<Object>();
+ for (String s : value.split(",")) {
+ list.add(parseAttribute(s.trim(), type));
+ }
+ return list;
+ } else {
+ throw new IllegalStateException("Unexpected type: '" + type + "'");
+ }
+ }
+
+ static XMLInputFactory factory;
+
+ private static synchronized XMLInputFactory getFactory() {
+ if (StaxParser.factory == null) {
+ XMLInputFactory factory = XMLInputFactory.newInstance();
+ factory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, true);
+ StaxParser.factory = factory;
+ }
+ return StaxParser.factory;
+ }
+
+ private StaxParser() {
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/karaf/blob/3574b440/features/core/src/main/java/org/apache/karaf/features/internal/repository/UrlLoader.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/repository/UrlLoader.java b/features/core/src/main/java/org/apache/karaf/features/internal/repository/UrlLoader.java
new file mode 100644
index 0000000..8bed863
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/repository/UrlLoader.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.features.internal.repository;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URLConnection;
+import java.util.zip.GZIPInputStream;
+
+import static java.net.HttpURLConnection.HTTP_NOT_MODIFIED;
+import static java.net.HttpURLConnection.HTTP_OK;
+
+/**
+ */
+public abstract class UrlLoader {
+
+ public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";
+ public static final String GZIP = "gzip";
+
+ private final String url;
+ private long lastModified;
+
+ public UrlLoader(String url) {
+ this.url = url;
+ }
+
+ protected boolean checkAndLoadCache() {
+ try {
+ URLConnection connection = new java.net.URL(url).openConnection();
+ if (connection instanceof HttpURLConnection) {
+ HttpURLConnection con = (HttpURLConnection) connection;
+ if (lastModified > 0) {
+ con.setIfModifiedSince(lastModified);
+ }
+ con.setRequestProperty(HEADER_ACCEPT_ENCODING, GZIP);
+ int rc = con.getResponseCode();
+ if (rc == HTTP_NOT_MODIFIED) {
+ return false;
+ }
+ if (rc != HTTP_OK) {
+ throw new IOException("Unexpected http response: " + rc + " " + con.getResponseMessage());
+ }
+ }
+ long lm = connection.getLastModified();
+ if (lm <= lastModified) {
+ return false;
+ }
+ BufferedInputStream bis = new BufferedInputStream(connection.getInputStream());
+ try {
+ // Auto-detect gzipped streams
+ InputStream is = bis;
+ bis.mark(512);
+ int b0 = bis.read();
+ int b1 = bis.read();
+ bis.reset();
+ if (b0 == 0x1f && b1 == 0x8b) {
+ is = new GZIPInputStream(bis);
+ }
+ boolean r = doRead(is);
+ lastModified = lm;
+ return r;
+ } finally {
+ bis.close();
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected abstract boolean doRead(InputStream is) throws IOException;
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/3574b440/features/core/src/main/java/org/apache/karaf/features/internal/repository/XmlRepository.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/repository/XmlRepository.java b/features/core/src/main/java/org/apache/karaf/features/internal/repository/XmlRepository.java
new file mode 100644
index 0000000..6661fb1
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/repository/XmlRepository.java
@@ -0,0 +1,163 @@
+/*
+ * 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.features.internal.repository;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import javax.xml.stream.XMLStreamException;
+
+import org.apache.karaf.features.internal.resolver.CapabilitySet;
+import org.apache.karaf.features.internal.resolver.SimpleFilter;
+import org.osgi.framework.Version;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+
+import static org.osgi.framework.namespace.IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE;
+import static org.osgi.framework.namespace.IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE;
+import static org.osgi.framework.namespace.IdentityNamespace.IDENTITY_NAMESPACE;
+
+/**
+ * Repository conforming to the OSGi Repository specification.
+ * The content of the URL can be gzipped.
+ */
+public class XmlRepository extends BaseRepository {
+
+ private final String url;
+ private final Map<String, XmlLoader> loaders = new HashMap<String, XmlLoader>();
+ private final ReadWriteLock lock = new ReentrantReadWriteLock();
+
+ public XmlRepository(String url) {
+ this.url = url;
+ }
+
+ @Override
+ public List<Resource> getResources() {
+ checkAndLoadCache();
+ return super.getResources();
+ }
+
+ @Override
+ public Map<Requirement, Collection<Capability>> findProviders(Collection<? extends Requirement> requirements) {
+ checkAndLoadCache();
+ return super.findProviders(requirements);
+ }
+
+ @Override
+ protected void addResource(Resource resource) {
+ List<Capability> identities = resource.getCapabilities(IDENTITY_NAMESPACE);
+ if (identities.isEmpty()) {
+ throw new IllegalStateException("Invalid resource: a capability with 'osgi.identity' namespace is required");
+ } else if (identities.size() > 1) {
+ throw new IllegalStateException("Invalid resource: multiple 'osgi.identity' capabilities found");
+ }
+ Capability identity = identities.get(0);
+ Object name = identity.getAttributes().get(IDENTITY_NAMESPACE);
+ Object type = identity.getAttributes().get(CAPABILITY_TYPE_ATTRIBUTE);
+ Object vers = identity.getAttributes().get(CAPABILITY_VERSION_ATTRIBUTE);
+ if (!String.class.isInstance(name)
+ || !String.class.isInstance(type)
+ || !Version.class.isInstance(vers)) {
+ throw new IllegalStateException("Invalid osgi.identity capability: " + identity);
+ }
+ if (!hasResource((String) type, (String) name, (Version) vers)) {
+ super.addResource(resource);
+ }
+ }
+
+ private boolean hasResource(String type, String name, Version version) {
+ CapabilitySet set = capSets.get(IDENTITY_NAMESPACE);
+ if (set != null) {
+ Map<String, Object> attrs = new HashMap<String, Object>();
+ attrs.put(CAPABILITY_TYPE_ATTRIBUTE, type);
+ attrs.put(IDENTITY_NAMESPACE, name);
+ attrs.put(CAPABILITY_VERSION_ATTRIBUTE, version);
+ SimpleFilter sf = SimpleFilter.convert(attrs);
+ return !set.match(sf, true).isEmpty();
+ } else {
+ return false;
+ }
+ }
+
+ private void checkAndLoadCache() {
+ if (checkAndLoadReferrals(url, Integer.MAX_VALUE)) {
+ lock.writeLock().lock();
+ try {
+ resources.clear();
+ capSets.clear();
+ populate(loaders.get(url).xml, Integer.MAX_VALUE);
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+ }
+
+ private void populate(StaxParser.XmlRepository xml, int hopCount) {
+ if (hopCount > 0) {
+ for (Resource resource : xml.resources) {
+ addResource(resource);
+ }
+ for (StaxParser.Referral referral : xml.referrals) {
+ populate(loaders.get(referral.url).xml, Math.min(referral.depth, hopCount - 1));
+ }
+ }
+ }
+
+ private boolean checkAndLoadReferrals(String url, int hopCount) {
+ boolean modified = false;
+ if (hopCount > 0) {
+ XmlLoader loader = loaders.get(url);
+ if (loader == null) {
+ loader = new XmlLoader(url);
+ loaders.put(url, loader);
+ }
+ modified = loader.checkAndLoadCache();
+ for (StaxParser.Referral referral : loader.xml.referrals) {
+ modified |= checkAndLoadReferrals(referral.url, Math.min(referral.depth, hopCount - 1));
+ }
+ }
+ return modified;
+ }
+
+ static class XmlLoader extends UrlLoader {
+
+ StaxParser.XmlRepository xml;
+
+ XmlLoader(String url) {
+ super(url);
+ }
+
+ @Override
+ protected boolean doRead(InputStream is) throws IOException {
+ try {
+ StaxParser.XmlRepository oldXml = xml;
+ xml = StaxParser.parse(is, oldXml);
+ return oldXml != xml;
+ } catch (XMLStreamException e) {
+ throw new IOException("Unable to read xml repository", e);
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/3574b440/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceImpl.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceImpl.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceImpl.java
index 873720e..cd48ca2 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceImpl.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceImpl.java
@@ -34,6 +34,15 @@ public class ResourceImpl implements Resource {
private final List<Capability> m_caps;
private final List<Requirement> m_reqs;
+ /**
+ * CAUTION: This constructor does not ensure that the resource
+ * has the required identity capability
+ */
+ public ResourceImpl() {
+ m_caps = new ArrayList<Capability>();
+ m_reqs = new ArrayList<Requirement>();
+ }
+
public ResourceImpl(String name, Version version) {
this(name, IdentityNamespace.TYPE_BUNDLE, version);
}
http://git-wip-us.apache.org/repos/asf/karaf/blob/3574b440/features/core/src/test/java/org/apache/karaf/features/internal/repository/RepositoryTest.java
----------------------------------------------------------------------
diff --git a/features/core/src/test/java/org/apache/karaf/features/internal/repository/RepositoryTest.java b/features/core/src/test/java/org/apache/karaf/features/internal/repository/RepositoryTest.java
new file mode 100644
index 0000000..c05eea0
--- /dev/null
+++ b/features/core/src/test/java/org/apache/karaf/features/internal/repository/RepositoryTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.features.internal.repository;
+
+import java.net.URL;
+
+import org.junit.Test;
+import org.osgi.resource.Resource;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.osgi.framework.namespace.BundleNamespace.BUNDLE_NAMESPACE;
+import static org.osgi.framework.namespace.IdentityNamespace.IDENTITY_NAMESPACE;
+import static org.osgi.framework.namespace.PackageNamespace.PACKAGE_NAMESPACE;
+
+public class RepositoryTest {
+
+ @Test
+ public void testXml() throws Exception {
+ URL url = getClass().getResource("repo.xml");
+ XmlRepository repo = new XmlRepository(url.toExternalForm());
+ verify(repo);
+ }
+
+ @Test
+ public void testJson() throws Exception {
+ URL url = getClass().getResource("repo.json");
+ JsonRepository repo = new JsonRepository(url.toExternalForm());
+ verify(repo);
+ }
+
+ private void verify(BaseRepository repo) {
+ assertNotNull(repo.getResources());
+ assertEquals(1, repo.getResources().size());
+ Resource resource = repo.getResources().get(0);
+ assertNotNull(resource);
+ assertEquals(1, resource.getCapabilities(IDENTITY_NAMESPACE).size());
+ assertEquals(1, resource.getCapabilities(BUNDLE_NAMESPACE).size());
+ assertEquals(1, resource.getCapabilities(PACKAGE_NAMESPACE).size());
+ assertEquals(1, resource.getRequirements(PACKAGE_NAMESPACE).size());
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/3574b440/features/core/src/test/resources/org/apache/karaf/features/internal/repository/repo.json
----------------------------------------------------------------------
diff --git a/features/core/src/test/resources/org/apache/karaf/features/internal/repository/repo.json b/features/core/src/test/resources/org/apache/karaf/features/internal/repository/repo.json
new file mode 100644
index 0000000..7249594
--- /dev/null
+++ b/features/core/src/test/resources/org/apache/karaf/features/internal/repository/repo.json
@@ -0,0 +1,9 @@
+ {
+ "http://www.acme.com/repository/org/acme/pool/org.acme.pool-1.5.6.jar": {
+ "Bundle-ManifestVersion": "2",
+ "Bundle-SymbolicName": "org.acme.pool",
+ "Bundle-Version": "1.5.6",
+ "Export-Package": "org.acme.pool; version=1.1.2; uses=\"org.acme.pool,org.acme.util\"",
+ "Import-Package": "org.apache.commons.pool; version=1.5.6"
+ }
+ }
http://git-wip-us.apache.org/repos/asf/karaf/blob/3574b440/features/core/src/test/resources/org/apache/karaf/features/internal/repository/repo.xml
----------------------------------------------------------------------
diff --git a/features/core/src/test/resources/org/apache/karaf/features/internal/repository/repo.xml b/features/core/src/test/resources/org/apache/karaf/features/internal/repository/repo.xml
new file mode 100644
index 0000000..dc1318b
--- /dev/null
+++ b/features/core/src/test/resources/org/apache/karaf/features/internal/repository/repo.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<repository name='OSGi Repository' increment='13582741'
+ xmlns='http://www.osgi.org/xmlns/repository/v1.0.0'>
+ <resource>
+ <capability namespace='osgi.identity'>
+ <attribute name='osgi.identity' value='org.acme.pool'/>
+ <attribute name='version' type='Version' value='1.5.6'> </attribute>
+ <attribute name='type' value='osgi.bundle'/>
+ </capability>
+ <capability namespace='osgi.content'>
+ <attribute name='osgi.content' value='e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'/>
+ <attribute name='url' value='http://www.acme.com/repository/org/acme/pool/org.acme.pool-1.5.6.jar'/>
+ <attribute name='size' type='Long' value='4405'/>
+ <attribute name='mime' value='application/vnd.osgi.bundle'/>
+ </capability>
+ <capability namespace='osgi.wiring.bundle'>
+ <attribute name='osgi.wiring.bundle' value='org.acme.pool'/>
+ <attribute name='bundle-version' type='Version' value='1.5.6'/>
+ </capability>
+ <capability namespace='osgi.wiring.package'>
+ <attribute name='osgi.wiring.package' value='org.acme.pool'/>
+ <attribute name='version' type='Version' value='1.1.2'/>
+ <attribute name='bundle-version' type='Version' value='1.5.6'/>
+ <attribute name='bundle-symbolic-name' value='org.acme.pool'/>
+ <directive name='uses' value='org.acme.pool,org.acme.util'/>
+ </capability>
+ <requirement namespace='osgi.wiring.package'>
+ <directive name='filter' value='(&(osgi.wiring.package=org.apache.commons.pool)(version>=1.5.6))'/>
+ </requirement>
+ <requirement namespace='osgi.identity'>
+ <directive name='effective' value='meta'/>
+ <directive name='resolution' value='optional'/>
+ <directive name='filter' value='(&(version=1.5.6)(osgi.identity=org.acme.pool-src))'/>
+ <directive name='classifier' value='sources'/>
+ </requirement>
+ </resource>
+</repository>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/karaf/blob/3574b440/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 82427e0..51a1eb3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2250,6 +2250,8 @@
<exclude>manual/**/*.conf</exclude>
<!-- test manifests -->
<exclude>**/*.mf</exclude>
+ <!-- test json files -->
+ <exclude>**/*.json</exclude>
</excludes>
</configuration>
</plugin>