ACE-536: Updated AgentDeploymentServlet to work with a Repository instead of using XPath expressions on the repository.xml file
This closes #12
git-svn-id: https://svn.apache.org/repos/asf/ace/trunk@1732498 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/org.apache.ace.agent.update.itest/src/org/apache/ace/agent/itest/AgentUpdateTest.java b/org.apache.ace.agent.update.itest/src/org/apache/ace/agent/itest/AgentUpdateTest.java
index dceda8c..3168ee0 100644
--- a/org.apache.ace.agent.update.itest/src/org/apache/ace/agent/itest/AgentUpdateTest.java
+++ b/org.apache.ace.agent.update.itest/src/org/apache/ace/agent/itest/AgentUpdateTest.java
@@ -23,7 +23,6 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
@@ -141,27 +140,19 @@
path = "/";
}
- if ("/repository.xml".equals(path)) {
- PrintWriter w = resp.getWriter();
- w.println("<?xml version='1.0' encoding='utf-8'?><repository>");
- w.println(createResource("org.apache.ace.agent", m_currentVersion));
- w.println(createResource("org.apache.ace.agent", m_nextVersion));
- w.println("</repository>");
+ String currentAgentJAR = m_currentVersion + ".jar";
+ String nextAgentJAR = m_nextVersion + ".jar";
+
+ if (path.endsWith(currentAgentJAR)) {
+ write(getBundle(), m_currentVersion, resp.getOutputStream());
+ }
+ else if (path.endsWith(nextAgentJAR)) {
+ write(getBundle(), m_nextVersion, resp.getOutputStream());
}
else {
- String currentAgentJAR = m_currentVersion + ".jar";
- String nextAgentJAR = m_nextVersion + ".jar";
-
- if (path.endsWith(currentAgentJAR)) {
- write(getBundle(), m_currentVersion, resp.getOutputStream());
- }
- else if (path.endsWith(nextAgentJAR)) {
- write(getBundle(), m_nextVersion, resp.getOutputStream());
- }
- else {
- throw new Error("Statement should never be reached.");
- }
+ throw new Error("Statement should never be reached.");
}
+
}
protected void doHead(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
@@ -213,10 +204,6 @@
CORRUPT_STREAM, BUNDLE_DOES_NOT_RESOLVE, BUNDLE_DOES_NOT_START, BUNDLE_WORKS
}
- private static String createResource(String bsn, String version) {
- return "<resource id='" + bsn + "/" + version + "' symbolicname='" + bsn + "' version='" + version + "' uri='" + bsn + "-" + version + ".jar'></resource>";
- }
-
private volatile HttpService m_http;
private volatile AgentUpdateOBRServlet m_servlet;
diff --git a/org.apache.ace.deployment/bnd.bnd b/org.apache.ace.deployment/bnd.bnd
index c6872bb..8608dc8 100644
--- a/org.apache.ace.deployment/bnd.bnd
+++ b/org.apache.ace.deployment/bnd.bnd
@@ -9,7 +9,11 @@
org.apache.felix.http.servlet-api,\
org.apache.felix.dependencymanager,\
com.vaadin,\
+ biz.aQute.repository,\
+ biz.aQute.bnd,\
org.apache.ace.authentication.api;version=latest,\
+ org.apache.ace.bnd.registry;version=latest,\
+ org.apache.ace.bnd.repository;version=latest,\
org.apache.ace.identification.api;version=latest,\
org.apache.ace.discovery.api;version=latest,\
org.apache.ace.connectionfactory;version=latest,\
diff --git a/org.apache.ace.deployment/servlet.bnd b/org.apache.ace.deployment/servlet.bnd
index 1175e9e..93fd9ff 100644
--- a/org.apache.ace.deployment/servlet.bnd
+++ b/org.apache.ace.deployment/servlet.bnd
@@ -1,8 +1,24 @@
# Licensed to the Apache Software Foundation (ASF) under the terms of ASLv2 (http://www.apache.org/licenses/LICENSE-2.0).
-Private-Package: org.apache.ace.deployment.servlet
+Private-Package: \
+ org.apache.ace.deployment.servlet,\
+ org.apache.ace.bnd.registry,\
+ org.apache.ace.bnd.repository,\
+ org.kxml2.io,\
+ org.osgi.impl.bundle.bindex,\
+ org.osgi.impl.bundle.obr.resource,\
+ org.osgi.service.bindex*;-split-package:=merge-last,\
+ org.osgi.service.indexer*,\
+ org.osgi.service.obr,\
+ org.osgi.service.repository*,\
+ org.xmlpull.v1
+
Bundle-Activator: org.apache.ace.deployment.servlet.Activator
Export-Package: org.apache.ace.deployment.processor
Bundle-Version: 1.0.3
Bundle-Name: Apache ACE Deployment Servlet
-Bundle-Description: Registers the Deployment Servlet
\ No newline at end of file
+Bundle-Description: Registers the Deployment Servlet
+
+Conditional-Package: \
+ biz.aQute*,\
+ aQute.*,\
\ No newline at end of file
diff --git a/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/AgentDeploymentServlet.java b/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/AgentDeploymentServlet.java
index f2a55b9..27ac522 100644
--- a/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/AgentDeploymentServlet.java
+++ b/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/AgentDeploymentServlet.java
@@ -18,37 +18,46 @@
*/
package org.apache.ace.deployment.servlet;
-import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
+import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import javax.xml.xpath.XPath;
-import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
-import javax.xml.xpath.XPathFactory;
+import org.apache.ace.bnd.registry.RegistryImpl;
+import org.apache.ace.bnd.repository.AceUrlConnector;
import org.apache.ace.connectionfactory.ConnectionFactory;
import org.osgi.framework.Version;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
import org.osgi.service.log.LogService;
-import org.w3c.dom.NamedNodeMap;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.xml.sax.InputSource;
+import org.osgi.service.repository.ContentNamespace;
+import org.osgi.service.repository.Repository;
+
+import aQute.bnd.deployer.repository.FixedIndexedRepo;
+import aQute.bnd.osgi.resource.CapReqBuilder;
+import aQute.bnd.service.Registry;
public class AgentDeploymentServlet extends HttpServlet implements ManagedService {
private static final long serialVersionUID = 1L;
@@ -58,8 +67,6 @@
/** URL to the OBR that is used for finding versions of the agent. */
private static final String KEY_OBR_URL = "obr.url";
- private static final String XPATH_QUERY = "/repository/resource[@uri]";
-
public static final String VERSIONS = "versions";
public static final String BUNDLE_MIMETYPE = "application/octet-stream";
public static final String TEXT_MIMETYPE = "text/plain";
@@ -72,30 +79,13 @@
private final String m_repositoryXML = "index.xml";
- /**
- * Gets the actual text from a named item contained in the given node map.
- *
- * @param map
- * the node map to get the named item from;
- * @param name
- * the name of the item to get.
- * @return the text of the named item, can be <code>null</code> in case the named item does not exist, or has no
- * text.
- */
- private static String getNamedItemText(NamedNodeMap map, String name) {
- Node namedItem = map.getNamedItem(name);
- if (namedItem == null) {
- return null;
- }
- else {
- return namedItem.getTextContent();
- }
- }
-
@Override
public void updated(Dictionary<String, ?> settings) throws ConfigurationException {
if (settings != null) {
String obrURL = (String) settings.get(KEY_OBR_URL);
+ if (obrURL == null) {
+ throw new ConfigurationException(KEY_OBR_URL, "Missing value!");
+ }
try {
URL url = new URL(obrURL);
m_obrURL = url;
@@ -103,10 +93,6 @@
catch (MalformedURLException e) {
throw new ConfigurationException(KEY_OBR_URL, "Invalid value, not a URL.", e);
}
- if (obrURL == null) {
- throw new ConfigurationException(KEY_OBR_URL, "Missing " +
- "value!");
- }
}
else {
m_obrURL = null;
@@ -138,90 +124,37 @@
protected URLConnection openConnection(URL url) throws IOException {
return m_connectionFactory.createConnection(url);
}
-
- private void closeSilently(Closeable resource) {
- try {
- if (resource != null) {
- resource.close();
+
+ private InputStream getAgentFromOBR(URL obrBaseUrl, String agentID, Version version) throws IOException {
+ Repository repository = createRepository();
+
+ Requirement requirement = new CapReqBuilder("osgi.identity")
+ .addDirective("filter", String.format("(&(osgi.identity=%s)(version=%s)(type=*))", agentID, version))
+ .buildSyntheticRequirement();
+
+ Map<Requirement, Collection<Capability>> sourceResources = repository.findProviders(Collections.singleton(requirement));
+ if (sourceResources.isEmpty() || sourceResources.get(requirement).isEmpty()) {
+ return null;
+ }
+
+ Iterator<Capability> capabilities = sourceResources.get(requirement).iterator();
+ while (capabilities.hasNext()) {
+ Capability capability = capabilities.next();
+ Resource resource = capability.getResource();
+
+ List<Capability> contentCapabilities = resource.getCapabilities(ContentNamespace.CONTENT_NAMESPACE);
+ if (contentCapabilities != null && contentCapabilities.size() == 1) {
+ Capability content = contentCapabilities.get(0);
+ URI uri = (URI) content.getAttributes().get(ContentNamespace.CAPABILITY_URL_ATTRIBUTE);
+ if (uri != null) {
+ return m_connectionFactory.createConnection(uri.toURL()).getInputStream();
+ }
}
}
- catch (IOException e) {
- m_log.log(LogService.LOG_WARNING, "Exception trying to close stream after request. ", e);
- throw new RuntimeException(e);
- }
- }
-
- private URL createOBRURL() throws MalformedURLException {
- try {
- return new URL(m_obrURL, m_repositoryXML);
- }
- catch (MalformedURLException e) {
- m_log.log(LogService.LOG_ERROR, "Error retrieving index.xml from " + m_obrURL);
- throw e;
- }
- }
-
- private InputStream getAgentFromOBR(URL obrBaseUrl, String agentID, Version version) throws XPathExpressionException, IOException {
- InputStream input = null;
- NodeList resources = getOBRNodeList(input);
- for (int nResource = 0; nResource < resources.getLength(); nResource++) {
- Node resource = resources.item(nResource);
- NamedNodeMap attr = resource.getAttributes();
-
- String uri = getNamedItemText(attr, "uri");
- if (uri == null || uri.equals("")) {
- m_log.log(LogService.LOG_ERROR, "Skipping resource without uri from repository " + obrBaseUrl);
- continue;
- }
-
- String symbolicname = getNamedItemText(attr, "symbolicname");
- Version bundleVersion = new Version(getNamedItemText(attr, "version"));
- if (agentID.equals(symbolicname) && version.equals(bundleVersion)) {
- URL url = new URL(obrBaseUrl, getNamedItemText(attr, "uri"));
- URLConnection connection = openConnection(url);
- return connection.getInputStream();
- }
- }
+
return null;
}
- private NodeList getOBRNodeList(InputStream input) throws XPathExpressionException, IOException {
- NodeList resources;
- try {
- URLConnection connection = openConnection(createOBRURL());
- // We always want the newest index.xml file.
- connection.setUseCaches(false);
-
- input = connection.getInputStream();
-
- try {
- XPath xpath = XPathFactory.newInstance().newXPath();
- // this XPath expressing will find all 'resource' elements which
- // have an attribute 'uri'.
- resources = (NodeList) xpath.evaluate(XPATH_QUERY, new InputSource(input), XPathConstants.NODESET);
- }
- catch (XPathExpressionException e) {
- m_log.log(LogService.LOG_ERROR, "Error evaluating XPath expression.", e);
- throw e;
- }
- }
- catch (IOException e) {
- m_log.log(LogService.LOG_ERROR, "Error reading repository metadata.", e);
- throw e;
- }
- finally {
- if (input != null) {
- try {
- input.close();
- }
- catch (IOException e) {
- // too bad, no worries.
- }
- }
- }
- return resources;
- }
-
private List<Version> getVersions(String agentID) throws AceRestException {
try {
return getVersionsFromOBR(m_obrURL, agentID);
@@ -236,56 +169,87 @@
}
private List<Version> getVersionsFromOBR(URL obrBaseUrl, String agentID) throws XPathExpressionException, IOException {
- InputStream input = null;
- NodeList resources = getOBRNodeList(input);
- List<Version> obrList = new ArrayList<>();
- for (int nResource = 0; nResource < resources.getLength(); nResource++) {
- Node resource = resources.item(nResource);
- NamedNodeMap attr = resource.getAttributes();
-
- String uri = getNamedItemText(attr, "uri");
- if (uri == null || uri.equals("")) {
- m_log.log(LogService.LOG_ERROR, "Skipping resource without uri from repository " + obrBaseUrl);
- continue;
+ Repository repository = createRepository();
+
+ Requirement requirement = new CapReqBuilder("osgi.identity")
+ .addDirective("filter", String.format("(&(osgi.identity=%s)(version=*)(type=*))", agentID))
+ .buildSyntheticRequirement();
+
+ Map<Requirement, Collection<Capability>> sourceResources = repository.findProviders(Collections.singleton(requirement));
+ if (sourceResources.isEmpty() || sourceResources.get(requirement).isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ Iterator<Capability> capabilities = sourceResources.get(requirement).iterator();
+ List<Version> versions = new ArrayList<>();
+
+ while (capabilities.hasNext()) {
+ Capability capability = capabilities.next();
+
+ Resource resource = capability.getResource();
+ List<Capability> identities = resource.getCapabilities(IdentityNamespace.IDENTITY_NAMESPACE);
+ Version version = null;
+ if (identities != null && identities.size() == 1){
+ Capability id = identities.get(0);
+ version = (Version) id.getAttributes().get(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE);
}
-
- String symbolicname = getNamedItemText(attr, "symbolicname");
- if (agentID.equals(symbolicname)) {
- Version version = new Version(getNamedItemText(attr, "version"));
- obrList.add(version);
+
+ URI uri = null;
+ List<Capability> contentCapabilities = resource.getCapabilities(ContentNamespace.CONTENT_NAMESPACE);
+ if (contentCapabilities != null && contentCapabilities.size() == 1) {
+ Capability content = contentCapabilities.get(0);
+ uri = (URI) content.getAttributes().get(ContentNamespace.CAPABILITY_URL_ATTRIBUTE);
+ }
+
+ if (version != null && uri != null) {
+ versions.add(version);
}
}
- Collections.sort(obrList);
- return obrList;
+
+ return versions;
}
- private void handlePackageDelivery(String agentID, Version version, HttpServletRequest request, HttpServletResponse response) throws AceRestException {
- InputStream is = null;
- OutputStream os = null;
-
+ private Repository createRepository() throws MalformedURLException {
+ FixedIndexedRepo fixedIndexedRepo = new FixedIndexedRepo();
+
+ AceUrlConnector aceUrlConnector = new AceUrlConnector(m_connectionFactory);
+ Registry registry = new RegistryImpl(aceUrlConnector);
+ fixedIndexedRepo.setRegistry(registry);
+
+ Map<String, String> properties = new HashMap<>();
+ properties.put(FixedIndexedRepo.PROP_LOCATIONS, createOBRURL().toString());
+ fixedIndexedRepo.setProperties(properties);
+ return fixedIndexedRepo;
+ }
+
+ private URL createOBRURL() throws MalformedURLException {
try {
+ return new URL(m_obrURL, m_repositoryXML);
+ }
+ catch (MalformedURLException e) {
+ m_log.log(LogService.LOG_ERROR, "Error retrieving index.xml from " + m_obrURL);
+ throw e;
+ }
+ }
+
+ private void handlePackageDelivery(String agentID, Version version, HttpServletRequest request, HttpServletResponse response) throws AceRestException {
+ try (InputStream is = getAgentFromOBR(m_obrURL, agentID, version)){
+ if (is == null) {
+ throw (AceRestException) new AceRestException(HttpServletResponse.SC_NOT_FOUND, "Agent not found in OBR.");
+ }
+
// Wrap response to add support for range requests
response = new ContentRangeResponseWrapper(request, response);
-
- try {
- is = getAgentFromOBR(m_obrURL, agentID, version);
- if (is == null) {
- throw (AceRestException) new AceRestException(HttpServletResponse.SC_NOT_FOUND, "Agent not found in OBR.");
- }
- }
- catch (XPathExpressionException e) {
- throw (AceRestException) new AceRestException(HttpServletResponse.SC_NOT_FOUND, "Agent not found: error parsing OBR").initCause(e);
- }
-
response.setContentType(BUNDLE_MIMETYPE);
- os = response.getOutputStream();
- byte[] buffer = new byte[BUFFER_SIZE];
- int bytes;
- while ((bytes = is.read(buffer)) != -1) {
- os.write(buffer, 0, bytes);
+ try (OutputStream os = response.getOutputStream()) {
+ byte[] buffer = new byte[BUFFER_SIZE];
+ int bytes;
+ while ((bytes = is.read(buffer)) != -1) {
+ os.write(buffer, 0, bytes);
+ }
+ os.flush();
}
- os.flush();
}
catch (IllegalArgumentException e) {
throw (AceRestException) new AceRestException(HttpServletResponse.SC_BAD_REQUEST, "Request URI is invalid").initCause(e);
@@ -293,18 +257,11 @@
catch (IOException e) {
throw (AceRestException) new AceRestException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Could not deliver package").initCause(e);
}
- finally {
- closeSilently(is);
- closeSilently(os);
- }
}
private void handleVersionsRequest(List<Version> versions, HttpServletResponse response) throws AceRestException {
- ServletOutputStream output = null;
-
response.setContentType(TEXT_MIMETYPE);
- try {
- output = response.getOutputStream();
+ try (ServletOutputStream output = response.getOutputStream()){
for (Version version : versions) {
output.print(version.toString());
output.print("\n");
@@ -313,9 +270,6 @@
catch (IOException e) {
throw new AceRestException(HttpServletResponse.SC_BAD_REQUEST, "Request URI is invalid");
}
- finally {
- closeSilently(output);
- }
}
private String[] verifyAndGetPathElements(String path) throws AceRestException {