Return-Path: X-Original-To: apmail-ace-commits-archive@www.apache.org Delivered-To: apmail-ace-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 08C9010AA5 for ; Fri, 16 Aug 2013 14:17:45 +0000 (UTC) Received: (qmail 24362 invoked by uid 500); 16 Aug 2013 14:17:44 -0000 Delivered-To: apmail-ace-commits-archive@ace.apache.org Received: (qmail 24335 invoked by uid 500); 16 Aug 2013 14:17:42 -0000 Mailing-List: contact commits-help@ace.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@ace.apache.org Delivered-To: mailing list commits@ace.apache.org Received: (qmail 24322 invoked by uid 99); 16 Aug 2013 14:17:40 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 16 Aug 2013 14:17:40 +0000 X-ASF-Spam-Status: No, hits=-1998.0 required=5.0 tests=ALL_TRUSTED,FB_GET_MEDS X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 16 Aug 2013 14:17:24 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 4856D238888A; Fri, 16 Aug 2013 14:16:57 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1514721 - in /ace/trunk: org.apache.ace.deployment.itest/src/org/apache/ace/it/deployment/ org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/ org.apache.ace.repository.itest/ run-server-allinone/ run-server-allinone/conf/ Date: Fri, 16 Aug 2013 14:16:57 -0000 To: commits@ace.apache.org From: marrs@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20130816141657.4856D238888A@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: marrs Date: Fri Aug 16 14:16:56 2013 New Revision: 1514721 URL: http://svn.apache.org/r1514721 Log: ACE-342 Added initial server side code to fetch versions and bundles. Added: ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/AgentDeploymentServlet.java ace/trunk/run-server-allinone/conf/org.apache.ace.deployment.servlet.agent.cfg Modified: ace/trunk/org.apache.ace.deployment.itest/src/org/apache/ace/it/deployment/DeploymentIntegrationTest.java ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/Activator.java ace/trunk/org.apache.ace.repository.itest/bnd.bnd ace/trunk/run-server-allinone/server-allinone.bndrun Modified: ace/trunk/org.apache.ace.deployment.itest/src/org/apache/ace/it/deployment/DeploymentIntegrationTest.java URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment.itest/src/org/apache/ace/it/deployment/DeploymentIntegrationTest.java?rev=1514721&r1=1514720&r2=1514721&view=diff ============================================================================== --- ace/trunk/org.apache.ace.deployment.itest/src/org/apache/ace/it/deployment/DeploymentIntegrationTest.java (original) +++ ace/trunk/org.apache.ace.deployment.itest/src/org/apache/ace/it/deployment/DeploymentIntegrationTest.java Fri Aug 16 14:16:56 2013 @@ -360,7 +360,7 @@ public class DeploymentIntegrationTest e private void configureServer() throws IOException { // configure data bundle - configure(org.apache.ace.deployment.servlet.Activator.PID, HttpConstants.ENDPOINT, "/deployment", "authentication.enabled", "false"); + configure(org.apache.ace.deployment.servlet.Activator.DEPLOYMENT_PID, HttpConstants.ENDPOINT, "/deployment", "authentication.enabled", "false"); // configure file based backend configure(org.apache.ace.deployment.provider.filebased.Activator.PID, "BaseDirectoryName", m_tempDir.getAbsolutePath()); } Modified: ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/Activator.java URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/Activator.java?rev=1514721&r1=1514720&r2=1514721&view=diff ============================================================================== --- ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/Activator.java (original) +++ ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/Activator.java Fri Aug 16 14:16:56 2013 @@ -20,6 +20,7 @@ package org.apache.ace.deployment.servle import javax.servlet.Servlet; +import org.apache.ace.connectionfactory.ConnectionFactory; import org.apache.ace.deployment.processor.DeploymentProcessor; import org.apache.ace.deployment.provider.DeploymentProvider; import org.apache.ace.deployment.streamgenerator.StreamGenerator; @@ -29,19 +30,27 @@ import org.osgi.framework.BundleContext; import org.osgi.service.log.LogService; public class Activator extends DependencyActivatorBase { - public static final String PID = "org.apache.ace.deployment.servlet"; + public static final String DEPLOYMENT_PID = "org.apache.ace.deployment.servlet"; + public static final String AGENT_PID = "org.apache.ace.deployment.servlet.agent"; @Override public void init(BundleContext context, DependencyManager manager) throws Exception { manager.add(createComponent() .setInterface(Servlet.class.getName(), null) .setImplementation(DeploymentServlet.class) - .add(createConfigurationDependency().setPropagate(true).setPid(PID)) + .add(createConfigurationDependency().setPropagate(true).setPid(DEPLOYMENT_PID)) .add(createServiceDependency().setService(StreamGenerator.class).setRequired(true)) .add(createServiceDependency().setService(DeploymentProvider.class).setRequired(true)) .add(createServiceDependency().setService(DeploymentProcessor.class).setRequired(false).setCallbacks("addProcessor", "removeProcessor")) .add(createServiceDependency().setService(LogService.class).setRequired(false)) ); + manager.add(createComponent() + .setInterface(Servlet.class.getName(), null) + .setImplementation(AgentDeploymentServlet.class) + .add(createConfigurationDependency().setPropagate(true).setPid(AGENT_PID)) + .add(createServiceDependency().setService(ConnectionFactory.class).setRequired(true)) + .add(createServiceDependency().setService(LogService.class).setRequired(false)) + ); } @Override Added: ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/AgentDeploymentServlet.java URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/AgentDeploymentServlet.java?rev=1514721&view=auto ============================================================================== --- ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/AgentDeploymentServlet.java (added) +++ ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/AgentDeploymentServlet.java Fri Aug 16 14:16:56 2013 @@ -0,0 +1,333 @@ +/* + * 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.ace.deployment.servlet; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Dictionary; +import java.util.List; + +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.authentication.api.AuthenticationService; +import org.apache.ace.connectionfactory.ConnectionFactory; +import org.apache.felix.dm.DependencyManager; +import org.osgi.framework.Version; +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; + +public class AgentDeploymentServlet extends HttpServlet implements ManagedService { + private static final int BUFFER_SIZE = 1024 * 32; + /** A boolean denoting whether or not authentication is enabled. */ + private static final String KEY_USE_AUTHENTICATION = "authentication.enabled"; + /** URL to the OBR that is used for finding versions of the agent. */ + private static final String KEY_OBR_URL = "obr.url"; + + public static final String VERSIONS = "versions"; + public static final String BUNDLE_MIMETYPE = "application/octet-stream"; + public static final String TEXT_MIMETYPE = "text/plain"; + + // injected by Dependency Manager + private volatile DependencyManager m_dm; + private volatile LogService m_log; + private volatile AuthenticationService m_authService; + private volatile ConnectionFactory m_connectionFactory; + + private volatile boolean m_useAuth = false; + private static final String XPATH_QUERY = "/repository/resource[@uri]"; + private final String m_repositoryXML = "repository.xml"; + private URL m_obrURL; + + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + try { + String[] pathElements = verifyAndGetPathElements(request.getPathInfo()); + String targetID = pathElements[1]; // in the future we might use this for per target approval + String agentID = pathElements[2]; + int numberOfElements = pathElements.length; + if (numberOfElements == 4) { + handleVersionsRequest(getVersions(agentID), response); + } + else { + handlePackageDelivery(agentID, new Version(pathElements[4]), request, response); + } + } + catch (AceRestException e) { + m_log.log(LogService.LOG_WARNING, e.getMessage(), e); + e.handleAsHttpError(response); + } + } + + private String[] verifyAndGetPathElements(String path) throws AceRestException { + if (path == null) { + throw new AceRestException(HttpServletResponse.SC_BAD_REQUEST, "Request URI is invalid, no path specified."); + } + String[] elements = path.split("/"); + int numberOfElements = elements.length; + if ((numberOfElements < 4) || (numberOfElements > 5) || !VERSIONS.equals(elements[3])) { + throw new AceRestException(HttpServletResponse.SC_BAD_REQUEST, "Request URI elements are invalid: " + path); + } + return elements; + } + + private List getVersions(String agentID) throws AceRestException { + try { + return getVersionsFromOBR(m_obrURL, agentID); + } + catch (XPathExpressionException e) { + throw new AceRestException(HttpServletResponse.SC_NOT_FOUND, "Unknown agent (" + agentID + ")"); + } + catch (IOException ioe) { + m_log.log(LogService.LOG_WARNING, "Error getting available versions.", ioe); + throw new AceRestException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Error getting available versions."); + } + } + + private List getVersionsFromOBR(URL obrBaseUrl, String agentID) throws XPathExpressionException, IOException { + InputStream input = null; + NodeList resources = getOBRNodeList(input); + List 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; + } + + String symbolicname = getNamedItemText(attr, "symbolicname"); + if (agentID.equals(symbolicname)) { + Version version = new Version(getNamedItemText(attr, "version")); + obrList.add(version); + } + } + Collections.sort(obrList); + return obrList; + } + 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 repository.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 URL createOBRURL() throws MalformedURLException { + try { + return new URL(m_obrURL, m_repositoryXML); + } + catch (MalformedURLException e) { + m_log.log(LogService.LOG_ERROR, "Error retrieving repository.xml from " + m_obrURL); + throw e; + } + } + protected URLConnection openConnection(URL url) throws IOException { + return m_connectionFactory.createConnection(url); + } + /** + * 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 null 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(); + } + } + + private void handleVersionsRequest(List versions, HttpServletResponse response) throws AceRestException { + ServletOutputStream output = null; + + response.setContentType(TEXT_MIMETYPE); + try { + output = response.getOutputStream(); + for (Version version : versions) { + output.print(version.toString()); + output.print("\n"); + } + } + catch (IOException e) { + throw new AceRestException(HttpServletResponse.SC_BAD_REQUEST, "Request URI is invalid"); + } + finally { + tryClose(output); + } + } + + private void handlePackageDelivery(final String agentID, final Version version, final HttpServletRequest request, final HttpServletResponse response) throws AceRestException { + ServletOutputStream output = null; + + try { + InputStream inputStream = null; + try { + inputStream = getAgentFromOBR(m_obrURL, agentID, version); + if (inputStream == 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); + } + finally { + if (inputStream != null) { + inputStream.close(); + } + } + response.setContentType(BUNDLE_MIMETYPE); + output = response.getOutputStream(); + byte[] buffer = new byte[BUFFER_SIZE]; + int bytes; + while ((bytes = inputStream.read(buffer)) != -1) { + output.write(buffer, 0, bytes); + } + } + catch (IllegalArgumentException e) { + throw (AceRestException) new AceRestException(HttpServletResponse.SC_BAD_REQUEST, "Request URI is invalid").initCause(e); + } + catch (IOException e) { + throw (AceRestException) new AceRestException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Could not deliver package").initCause(e); + } + finally { + tryClose(output); + } + } + + private void tryClose(OutputStream output) { + try { + if (output != null) { + output.close(); + } + } + catch (IOException e) { + m_log.log(LogService.LOG_WARNING, "Exception trying to close stream after request. ", e); + throw new RuntimeException(e); + } + } + + @Override + public void updated(Dictionary settings) throws ConfigurationException { + if (settings != null) { + String useAuthString = (String) settings.get(KEY_USE_AUTHENTICATION); + if (useAuthString == null + || !("true".equalsIgnoreCase(useAuthString) || "false".equalsIgnoreCase(useAuthString))) { + throw new ConfigurationException(KEY_USE_AUTHENTICATION, "Missing or invalid value!"); + } + boolean useAuth = Boolean.parseBoolean(useAuthString); + m_useAuth = useAuth; + + String obrURL = (String) settings.get(KEY_OBR_URL); + try { + URL url = new URL(obrURL); + m_obrURL = url; + } + 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_useAuth = false; + m_obrURL = null; + } + } +} Modified: ace/trunk/org.apache.ace.repository.itest/bnd.bnd URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.repository.itest/bnd.bnd?rev=1514721&r1=1514720&r2=1514721&view=diff ============================================================================== --- ace/trunk/org.apache.ace.repository.itest/bnd.bnd (original) +++ ace/trunk/org.apache.ace.repository.itest/bnd.bnd Fri Aug 16 14:16:56 2013 @@ -11,8 +11,7 @@ Test-Cases: ${classes;CONCRETE;EXTENDS;o org.apache.felix.dependencymanager -runfw: org.apache.felix.framework;version='[4,5)' -runvm: -ea --runbundles: \ - osgi.cmpn,\ +-runbundles: osgi.cmpn,\ org.apache.felix.log,\ org.apache.felix.dependencymanager,\ org.apache.felix.configadmin,\ @@ -25,7 +24,11 @@ Test-Cases: ${classes;CONCRETE;EXTENDS;o org.apache.ace.range.api;version=latest,\ org.apache.ace.repository.api;version=latest,\ org.apache.ace.repository.impl;version=latest,\ - org.apache.ace.repository.servlet;version=latest + org.apache.ace.repository.servlet;version=latest,\ + org.apache.felix.gogo.command,\ + org.apache.felix.gogo.runtime,\ + org.apache.felix.gogo.shell,\ + org.apache.felix.dependencymanager.shell Private-Package: org.apache.ace.it.repository Bundle-Version: 1.0.0 Bundle-Name: Apache ACE Repository itest Added: ace/trunk/run-server-allinone/conf/org.apache.ace.deployment.servlet.agent.cfg URL: http://svn.apache.org/viewvc/ace/trunk/run-server-allinone/conf/org.apache.ace.deployment.servlet.agent.cfg?rev=1514721&view=auto ============================================================================== --- ace/trunk/run-server-allinone/conf/org.apache.ace.deployment.servlet.agent.cfg (added) +++ ace/trunk/run-server-allinone/conf/org.apache.ace.deployment.servlet.agent.cfg Fri Aug 16 14:16:56 2013 @@ -0,0 +1,4 @@ +org.apache.ace.server.servlet.endpoint=/agent +# OBR settings +obr.url = http://localhost:${org.apache.ace.server.port}/obr/ +authentication.enabled = false Modified: ace/trunk/run-server-allinone/server-allinone.bndrun URL: http://svn.apache.org/viewvc/ace/trunk/run-server-allinone/server-allinone.bndrun?rev=1514721&r1=1514720&r2=1514721&view=diff ============================================================================== --- ace/trunk/run-server-allinone/server-allinone.bndrun (original) +++ ace/trunk/run-server-allinone/server-allinone.bndrun Fri Aug 16 14:16:56 2013 @@ -73,4 +73,4 @@ org.apache.felix.log.maxSize=1000,\ launch.keep=true,\ launch.storage.dir=bundle-cache --runvm: -Xmx1G \ No newline at end of file +-runvm: -Xmx1G -agentpath:/Applications/YourKit_Java_Profiler_12.0.5.app/bin/mac/libyjpagent.jnilib \ No newline at end of file