Move rest into a separate module, remove dependencies on http-components and commons-io Add support for gzip and last modification date on the servlet
Project: http://git-wip-us.apache.org/repos/asf/karaf-cave/repo Commit: http://git-wip-us.apache.org/repos/asf/karaf-cave/commit/a59c7a8e Tree: http://git-wip-us.apache.org/repos/asf/karaf-cave/tree/a59c7a8e Diff: http://git-wip-us.apache.org/repos/asf/karaf-cave/diff/a59c7a8e Branch: refs/heads/master Commit: a59c7a8e4cd57d21d821ba7e2914b622bacdd75e Parents: dc8ee60 Author: Guillaume Nodet <[email protected]> Authored: Tue May 5 10:12:56 2015 +0200 Committer: Guillaume Nodet <[email protected]> Committed: Tue May 5 10:12:56 2015 +0200 ---------------------------------------------------------------------- assembly/src/main/resources/features.xml | 42 +++-- .../karaf/cave/server/api/CaveRepository.java | 7 + .../karaf/cave/server/http/WrapperServlet.java | 75 +++++--- .../server/management/CaveRepositoryMBean.java | 2 +- server/pom.xml | 1 + server/rest/NOTICE | 29 +++ server/rest/pom.xml | 62 ++++++ .../OSGI-INF/blueprint/cave-server-rest.xml | 32 ++++ server/storage/pom.xml | 31 +-- .../cave/server/storage/CaveRepositoryImpl.java | 187 +++++++++---------- .../cave/server/storage/OsgiRepository.java | 58 ++++-- .../apache/karaf/cave/server/storage/Utils.java | 45 +++++ .../OSGI-INF/blueprint/cave-storage.xml | 15 -- .../server/storage/CaveRepositoryImplTest.java | 10 +- 14 files changed, 403 insertions(+), 193 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/karaf-cave/blob/a59c7a8e/assembly/src/main/resources/features.xml ---------------------------------------------------------------------- diff --git a/assembly/src/main/resources/features.xml b/assembly/src/main/resources/features.xml index 13b57fe..47ff868 100644 --- a/assembly/src/main/resources/features.xml +++ b/assembly/src/main/resources/features.xml @@ -21,24 +21,44 @@ <repository>mvn:org.apache.cxf.karaf/apache-cxf/${cxf.version}/xml/features</repository> <feature name="cave-server" version="${project.version}"> - <configfile finalname="/etc/org.apache.cxf.osgi.cfg"> - mvn:org.apache.karaf.cave/apache-karaf-cave/${project.version}/cfg/cxf - </configfile> - <feature version="[3,4)">cxf</feature> - <feature>obr</feature> - <feature>war</feature> + <feature>cave-storage</feature> + <feature>cave-http</feature> + <feature>cave-rest</feature> + </feature> + + <feature name="cave-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:commons-io/commons-io/${commons-io.version}</bundle> - <bundle>mvn:org.apache.httpcomponents/httpcore-osgi/4.3.2</bundle> - <bundle>mvn:org.apache.httpcomponents/httpclient-osgi/${httpclient.version}</bundle> + <feature>shell-compat</feature> + <bundle dependency="true">mvn:javax.annotation/javax.annotation-api/1.2</bundle> + <bundle dependency="true">mvn:org.apache.servicemix.specs/org.apache.servicemix.specs.jsr339-api-2.0/2.4.0</bundle> <bundle>mvn:org.jsoup/jsoup/${jsoup.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> - <bundle>mvn:org.apache.karaf.cave.server/org.apache.karaf.cave.server.management/${project.version}</bundle> - <bundle>mvn:org.apache.karaf.cave.server/org.apache.karaf.cave.server.command/${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-http" version="${project.version}"> + <feature>war</feature> + <feature>cave-server</feature> <bundle>mvn:org.apache.karaf.cave.server/org.apache.karaf.cave.server.http/${project.version}</bundle> </feature> + <feature name="cave-rest" version="${project.version}"> + <configfile finalname="/etc/org.apache.cxf.osgi.cfg"> + mvn:org.apache.karaf.cave/apache-karaf-cave/${project.version}/cfg/cxf + </configfile> + <feature version="[3,4)">cxf</feature> + <feature>cave-server</feature> + <bundle>mvn:org.apache.karaf.cave.server/org.apache.karaf.cave.server.rest/${project.version}</bundle> + </feature> + </features> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/karaf-cave/blob/a59c7a8e/server/api/src/main/java/org/apache/karaf/cave/server/api/CaveRepository.java ---------------------------------------------------------------------- diff --git a/server/api/src/main/java/org/apache/karaf/cave/server/api/CaveRepository.java b/server/api/src/main/java/org/apache/karaf/cave/server/api/CaveRepository.java index d7bff20..3713367 100644 --- a/server/api/src/main/java/org/apache/karaf/cave/server/api/CaveRepository.java +++ b/server/api/src/main/java/org/apache/karaf/cave/server/api/CaveRepository.java @@ -66,6 +66,13 @@ public abstract class CaveRepository { } /** + * Get the last modification date of this repository. + * + * @return the last modification date. + */ + public abstract long getIncrement(); + + /** * Upload an artifact from the given URL into the repository. * * @param url the URL of the artifact. http://git-wip-us.apache.org/repos/asf/karaf-cave/blob/a59c7a8e/server/http/src/main/java/org/apache/karaf/cave/server/http/WrapperServlet.java ---------------------------------------------------------------------- diff --git a/server/http/src/main/java/org/apache/karaf/cave/server/http/WrapperServlet.java b/server/http/src/main/java/org/apache/karaf/cave/server/http/WrapperServlet.java index 6b0428f..b219a4b 100644 --- a/server/http/src/main/java/org/apache/karaf/cave/server/http/WrapperServlet.java +++ b/server/http/src/main/java/org/apache/karaf/cave/server/http/WrapperServlet.java @@ -16,13 +16,13 @@ */ package org.apache.karaf.cave.server.http; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.net.URI; import java.net.URL; +import java.util.zip.GZIPOutputStream; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; @@ -40,7 +40,7 @@ import org.apache.karaf.cave.server.api.CaveRepository; import org.apache.karaf.cave.server.api.CaveRepositoryService; import org.apache.karaf.util.XmlUtils; import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -51,11 +51,23 @@ import org.xml.sax.SAXException; */ public class WrapperServlet extends HttpServlet { + public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding"; + public static final String GZIP = "gzip"; + private BundleContext bundleContext; + private ServiceTracker<CaveRepositoryService, CaveRepositoryService> tracker; public void init(ServletConfig servletConfig) throws ServletException { ServletContext context = servletConfig.getServletContext(); bundleContext = (BundleContext) context.getAttribute("osgi-bundlecontext"); + tracker = new ServiceTracker<CaveRepositoryService, CaveRepositoryService>(bundleContext, CaveRepositoryService.class, null); + tracker.open(); + } + + @Override + public void destroy() { + tracker.close(); + super.destroy(); } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { @@ -66,22 +78,33 @@ public class WrapperServlet extends HttpServlet { doIt(request, response); } - public void doIt(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - - ServiceReference caveRepositoryServiceReference = bundleContext.getServiceReference(CaveRepositoryService.class.getName()); - if (caveRepositoryServiceReference == null) { - throw new ServletException("CaveRepositoryService is not available"); + @Override + protected long getLastModified(HttpServletRequest request) { + String uri = request.getPathInfo(); + // remove the starting / + uri = uri.substring(1); + if (request.getPathInfo().endsWith("-repository.xml")) { + // the user wants to get the Cave repository repository.xml + // the expected format is {cave-repo-name}-repository.xml + int index = uri.indexOf("-repository.xml"); + String caveRepositoryName = uri.substring(0, index); + CaveRepositoryService caveRepositoryService = tracker.getService(); + if (caveRepositoryService != null) { + CaveRepository caveRepository = caveRepositoryService.getRepository(caveRepositoryName); + if (caveRepository != null) { + return caveRepository.getIncrement(); + } + } } - CaveRepositoryService caveRepositoryService = (CaveRepositoryService) bundleContext.getService(caveRepositoryServiceReference); + return -1; + } + + public void doIt(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + CaveRepositoryService caveRepositoryService = tracker.getService(); if (caveRepositoryService == null) { throw new ServletException("CaveRepositoryService is not available"); } - - try { - doIt2(caveRepositoryService, request, response); - } finally { - bundleContext.ungetService(caveRepositoryServiceReference); - } + doIt2(caveRepositoryService, request, response); } private void doIt2(CaveRepositoryService caveRepositoryService, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { @@ -120,10 +143,15 @@ public class WrapperServlet extends HttpServlet { } url = caveRepository.getRepositoryXml(); response.setContentType("text/xml"); - String repository = resolveRelativeUrls(url, request.getRequestURL().toString()); - response.getOutputStream().print(repository); - response.getOutputStream().flush(); - response.getOutputStream().close(); + + OutputStream os = response.getOutputStream(); + if (acceptsGZipEncoding(request)) { + os = new GZIPOutputStream(os); + response.addHeader("Content-Encoding", "gzip"); + } + resolveRelativeUrls(url, request.getRequestURL().toString(), os); + os.flush(); + os.close(); } else { for (CaveRepository repository : caveRepositoryService.getRepositories()) { URL resourceUrl = repository.getResourceByUri(uri); @@ -159,17 +187,20 @@ public class WrapperServlet extends HttpServlet { } } - private String resolveRelativeUrls(URL url, String baseUri) throws IOException, XMLStreamException, SAXException, ParserConfigurationException, TransformerException { + private boolean acceptsGZipEncoding(HttpServletRequest httpRequest) { + String acceptEncoding = httpRequest.getHeader("Accept-Encoding"); + return acceptEncoding != null && acceptEncoding.contains("gzip"); + } + + private void resolveRelativeUrls(URL url, String baseUri, OutputStream os) throws IOException, XMLStreamException, SAXException, ParserConfigurationException, TransformerException { // Read Document doc = XmlUtils.parse(url.toExternalForm()); // Transform resolveUrls(doc, baseUri); // Output DOMSource src = new DOMSource(doc); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - StreamResult res = new StreamResult(baos); + StreamResult res = new StreamResult(os); XmlUtils.transform(src, res); - return baos.toString(); } private void resolveUrls(Node node, String baseUri) { http://git-wip-us.apache.org/repos/asf/karaf-cave/blob/a59c7a8e/server/management/src/main/java/org/apache/karaf/cave/server/management/CaveRepositoryMBean.java ---------------------------------------------------------------------- diff --git a/server/management/src/main/java/org/apache/karaf/cave/server/management/CaveRepositoryMBean.java b/server/management/src/main/java/org/apache/karaf/cave/server/management/CaveRepositoryMBean.java index 326147d..3033468 100644 --- a/server/management/src/main/java/org/apache/karaf/cave/server/management/CaveRepositoryMBean.java +++ b/server/management/src/main/java/org/apache/karaf/cave/server/management/CaveRepositoryMBean.java @@ -16,7 +16,7 @@ package org.apache.karaf.cave.server.management; import javax.management.openmbean.TabularData; /** - * Cave repository MBean to management Cave repositories. + * Cave repository MBean to manage Cave repositories. */ public interface CaveRepositoryMBean { http://git-wip-us.apache.org/repos/asf/karaf-cave/blob/a59c7a8e/server/pom.xml ---------------------------------------------------------------------- diff --git a/server/pom.xml b/server/pom.xml index 2b2b64a..2e82021 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -39,6 +39,7 @@ <module>management</module> <module>command</module> <module>http</module> + <module>rest</module> </modules> </project> http://git-wip-us.apache.org/repos/asf/karaf-cave/blob/a59c7a8e/server/rest/NOTICE ---------------------------------------------------------------------- diff --git a/server/rest/NOTICE b/server/rest/NOTICE new file mode 100644 index 0000000..2bd5453 --- /dev/null +++ b/server/rest/NOTICE @@ -0,0 +1,29 @@ +Apache Karaf Cave +Copyright 2010-2014 The Apache Software Foundation + +I. Included Software + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). +Licensed under the Apache License 2.0. + +II. Used Software + +This product uses software developed at +The OSGi Alliance (http://www.osgi.org/). +Copyright (c) OSGi Alliance (2000, 2010). +Licensed under the Apache License 2.0. + +This product uses software developed at +OPS4J (http://www.ops4j.org/). +Licensed under the Apache License 2.0. + +This product uses software developed at +SLF4J (http://www.slf4j.org/). +Licensed under the MIT License. + +This product includes software from http://www.json.org. +Copyright (c) 2002 JSON.org + +III. License Summary +- Apache License 2.0 http://git-wip-us.apache.org/repos/asf/karaf-cave/blob/a59c7a8e/server/rest/pom.xml ---------------------------------------------------------------------- diff --git a/server/rest/pom.xml b/server/rest/pom.xml new file mode 100644 index 0000000..648be2c --- /dev/null +++ b/server/rest/pom.xml @@ -0,0 +1,62 @@ +<?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.server</artifactId> + <version>4.0.0-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> + </parent> + + <groupId>org.apache.karaf.cave.server</groupId> + <artifactId>org.apache.karaf.cave.server.rest</artifactId> + <name>Apache Karaf :: Cave :: Server :: Rest</name> + <packaging>bundle</packaging> + + <dependencies> + <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.cxf</groupId> + <artifactId>cxf-rt-frontend-jaxrs</artifactId> + </dependency> + </dependencies> + + <build> + <plugins> + <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> http://git-wip-us.apache.org/repos/asf/karaf-cave/blob/a59c7a8e/server/rest/src/main/resources/OSGI-INF/blueprint/cave-server-rest.xml ---------------------------------------------------------------------- diff --git a/server/rest/src/main/resources/OSGI-INF/blueprint/cave-server-rest.xml b/server/rest/src/main/resources/OSGI-INF/blueprint/cave-server-rest.xml new file mode 100644 index 0000000..be6e9d1 --- /dev/null +++ b/server/rest/src/main/resources/OSGI-INF/blueprint/cave-server-rest.xml @@ -0,0 +1,32 @@ +<?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. + +--> +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" + xmlns:jaxrs="http://cxf.apache.org/blueprint/jaxrs"> + + <reference id="caveRepositoryService" interface="org.apache.karaf.cave.server.api.CaveRepositoryService"/> + + <!-- start the JAX-RS server --> + <jaxrs:server id="caveRepositoryJaxRsServer" address="/cave"> + <jaxrs:serviceBeans> + <ref component-id="caveRepositoryService"/> + </jaxrs:serviceBeans> + </jaxrs:server> + +</blueprint> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/karaf-cave/blob/a59c7a8e/server/storage/pom.xml ---------------------------------------------------------------------- diff --git a/server/storage/pom.xml b/server/storage/pom.xml index 005f97e..00ca419 100644 --- a/server/storage/pom.xml +++ b/server/storage/pom.xml @@ -35,10 +35,6 @@ <dependencies> <dependency> - <groupId>commons-io</groupId> - <artifactId>commons-io</artifactId> - </dependency> - <dependency> <groupId>org.apache.karaf.cave.server</groupId> <artifactId>org.apache.karaf.cave.server.api</artifactId> <version>${project.version}</version> @@ -52,10 +48,6 @@ <artifactId>jsoup</artifactId> </dependency> <dependency> - <groupId>org.apache.httpcomponents</groupId> - <artifactId>httpclient-osgi</artifactId> - </dependency> - <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-frontend-jaxrs</artifactId> </dependency> @@ -74,30 +66,13 @@ <instructions> <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName> <Import-Package> - org.apache.karaf.cave.server.api;version="${project.version}", - org.slf4j*;version="[1.6,2)";resolution:=optional, - org.apache.felix.bundlerepository*;version="[2,3)", - org.osgi.framework;version="[1,4)", - org.osgi.service.blueprint, - org.osgi.service.log, - org.osgi.service.url, - org.apache.http*;version="[4,5)", - org.jsoup*;version="[1.6,2)", - org.apache.commons.io*;version="[2,3)", - !org.apache.felix.shell, - !org.apache.felix.bundlerepository.impl.wrapper, - !org.osgi.service.obr, - org.osgi.resource*, - org.osgi.service.repository*, - javax.xml.stream*, + !org.apache.karaf.features.internal.*, + * </Import-Package> <Private-Package> - org.kxml2.io, - org.xmlpull.v1, org.apache.felix.utils*, - org.apache.felix.bundlerepository.impl*, + org.apache.karaf.features.internal.resolver, org.apache.karaf.features.internal.repository, - org.apache.karaf.features.internal.resolver </Private-Package> </instructions> </configuration> http://git-wip-us.apache.org/repos/asf/karaf-cave/blob/a59c7a8e/server/storage/src/main/java/org/apache/karaf/cave/server/storage/CaveRepositoryImpl.java ---------------------------------------------------------------------- diff --git a/server/storage/src/main/java/org/apache/karaf/cave/server/storage/CaveRepositoryImpl.java b/server/storage/src/main/java/org/apache/karaf/cave/server/storage/CaveRepositoryImpl.java index d13c263..2ccef4e 100644 --- a/server/storage/src/main/java/org/apache/karaf/cave/server/storage/CaveRepositoryImpl.java +++ b/server/storage/src/main/java/org/apache/karaf/cave/server/storage/CaveRepositoryImpl.java @@ -17,12 +17,16 @@ package org.apache.karaf.cave.server.storage; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStreamWriter; +import java.io.Writer; +import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; import java.util.jar.Attributes; @@ -30,12 +34,6 @@ import java.util.jar.Manifest; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; -import org.apache.commons.io.FileUtils; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.DefaultHttpClient; import org.apache.karaf.cave.server.api.CaveRepository; import org.apache.karaf.features.internal.resolver.ResolverUtil; import org.apache.karaf.features.internal.resolver.ResourceBuilder; @@ -44,7 +42,6 @@ import org.jsoup.Jsoup; import org.jsoup.UnsupportedMimeTypeException; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; import org.osgi.framework.BundleException; import org.osgi.framework.Constants; import org.osgi.resource.Capability; @@ -68,33 +65,41 @@ public class CaveRepositoryImpl extends CaveRepository { public CaveRepositoryImpl(String name, String location, boolean scan) throws Exception { super(); - this.setName(name); - this.setLocation(location); + setName(name); + setLocation(location); - this.createRepositoryDirectory(); + createRepositoryDirectory(); if (scan) { scan(); - } else if (!getRepositoryXmlFile().exists()) { + } else if (!Files.exists(getRepositoryXmlFile())) { generateRepositoryXml(); } } + @Override + public long getIncrement() { + return repository.getIncrement(); + } + public OsgiRepository getRepository() { return repository; } + public Path getLocationPath() { + return Paths.get(getLocation()); + } + /** * Check if the repository folder exists and create it if not. */ private void createRepositoryDirectory() throws Exception { - LOGGER.debug("Create Cave repository {} folder.", this.getName()); - File locationFile = new File(this.getLocation()); - if (!locationFile.exists()) { - locationFile.mkdirs(); - LOGGER.debug("Cave repository {} location has been created.", this.getName()); - LOGGER.debug(locationFile.getAbsolutePath()); + LOGGER.debug("Create Cave repository {} folder.", getName()); + if (!Files.exists(getLocationPath())) { + Files.createDirectories(getLocationPath()); + LOGGER.debug("Cave repository {} location has been created.", getName()); + LOGGER.debug(getLocationPath().toAbsolutePath().toString()); } - repository = new OsgiRepository(getRepositoryXmlFile().toURI().toString(), getName()); + repository = new OsgiRepository(getRepositoryXmlFile().toUri().toString(), getName()); } /** @@ -103,11 +108,9 @@ public class CaveRepositoryImpl extends CaveRepository { * @throws Exception in case of repository.xml update failure. */ private void generateRepositoryXml() throws Exception { - File repositoryXml = this.getRepositoryXmlFile(); - OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(repositoryXml)); - repository.writeRepository(writer); - writer.flush(); - writer.close(); + try (Writer writer = Files.newBufferedWriter(getRepositoryXmlFile(), StandardCharsets.UTF_8)) { + repository.writeRepository(writer); + } } /** @@ -134,23 +137,25 @@ public class CaveRepositoryImpl extends CaveRepository { LOGGER.debug("Upload new artifact from {}", url); // TODO: this is problematic if receiving multiple requests at the same time String artifactName = "artifact-" + System.currentTimeMillis(); - File temp = new File(new File(this.getLocation()), artifactName); - FileUtils.copyURLToFile(url, temp); + Path temp = getLocationPath().resolve(artifactName); + try (InputStream is = url.openStream()) { + Files.copy(is, temp); + } // update the repository.xml - ResourceImpl resource = createResource(temp.toURI().toURL()); + ResourceImpl resource = createResource(temp.toUri().toURL()); if (resource == null) { - temp.delete(); + Files.delete(temp); LOGGER.warn("The {} artifact source is not a valid OSGi bundle", url); throw new IllegalArgumentException("The " + url.toString() + " artifact source is not a valid OSGi bundle"); } - File destination = new File(new File(getLocation()), ResolverUtil.getSymbolicName(resource) + "-" + ResolverUtil.getVersion(resource) + ".jar"); - if (destination.exists()) { - temp.delete(); + Path destination = getLocationPath().resolve(ResolverUtil.getSymbolicName(resource) + "-" + ResolverUtil.getVersion(resource) + ".jar"); + if (Files.exists(destination)) { + Files.delete(temp); LOGGER.warn("The {} artifact is already present in the Cave repository", url); throw new IllegalArgumentException("The " + url.toString() + " artifact is already present in the Cave repository"); } - FileUtils.moveFile(temp, destination); - resource = createResource(destination.toURI().toURL()); + Files.move(temp, destination); + resource = createResource(destination.toUri().toURL()); addResource(resource); generateRepositoryXml(); } @@ -176,8 +181,8 @@ public class CaveRepositoryImpl extends CaveRepository { if (entry.isDirectory()) { File[] children = entry.listFiles(); if (children != null) { - for (int i = 0; i < children.length; i++) { - scan(children[i]); + for (File child : children) { + scan(child); } } } else { @@ -217,13 +222,13 @@ public class CaveRepositoryImpl extends CaveRepository { if (url.getProtocol().equals("file")) { // filesystem proxyFilesystem (to another folder) File proxyFolder = new File(url.toURI()); - this.proxyFilesystem(proxyFolder, filter); + proxyFilesystem(proxyFolder, filter); } if (url.getProtocol().equals("http")) { // HTTP proxyFilesystem - this.proxyHttp(url.toExternalForm(), filter); + proxyHttp(url.toExternalForm(), filter); } - this.generateRepositoryXml(); + generateRepositoryXml(); } /** @@ -233,7 +238,7 @@ public class CaveRepositoryImpl extends CaveRepository { * @throws Exception */ public void proxy(URL url) throws Exception { - this.proxy(url, null); + proxy(url, null); } /** @@ -247,8 +252,10 @@ public class CaveRepositoryImpl extends CaveRepository { LOGGER.debug("Proxying filesystem {}", entry.getAbsolutePath()); if (entry.isDirectory()) { File[] children = entry.listFiles(); - for (int i = 0; i < children.length; i++) { - proxyFilesystem(children[i], filter); + if (children != null) { + for (File child : children) { + proxyFilesystem(child, filter); + } } } else { try { @@ -279,22 +286,19 @@ public class CaveRepositoryImpl extends CaveRepository { } Map<String, String> getHeaders(URL url) throws IOException { - InputStream is = url.openStream(); - try { + try (InputStream is = url.openStream()) { ZipInputStream zis = new ZipInputStream(is); ZipEntry entry; while ((entry = zis.getNextEntry()) != null) { if (MANIFEST_NAME.equals(entry.getName())) { Attributes attributes = new Manifest(zis).getMainAttributes(); - Map<String, String> headers = new HashMap<String, String>(); + Map<String, String> headers = new HashMap<>(); for (Map.Entry attr : attributes.entrySet()) { headers.put(attr.getKey().toString(), attr.getValue().toString()); } return headers; } } - } finally { - is.close(); } throw new IllegalArgumentException("Resource " + url + " does not contain a manifest"); } @@ -308,15 +312,11 @@ public class CaveRepositoryImpl extends CaveRepository { */ private void proxyHttp(String url, String filter) throws Exception { LOGGER.debug("Proxying HTTP URL {}", url); - HttpClient httpClient = new DefaultHttpClient(); - HttpGet httpGet = new HttpGet(url); - HttpResponse response = httpClient.execute(httpGet); - HttpEntity entity = response.getEntity(); - - if (entity != null) { - if (entity.getContentType().getValue().equals("application/java-archive") - || entity.getContentType().getValue().equals("application/octet-stream")) { + HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); + try (InputStream is = conn.getInputStream()) { + String type = conn.getContentType(); + if ("application/java-archive".equals(type) || "application/octet-stream".equals(type)) { // I have a jar/binary, potentially a resource try { if ((filter == null) || (url.matches(filter))) { @@ -333,13 +333,10 @@ public class CaveRepositoryImpl extends CaveRepository { // try to find link to "browse" try { Document document = Jsoup.connect(url).get(); - - Elements links = document.select("a"); - if (links.size() > 1) { - for (int i = 1; i < links.size(); i++) { - Element link = links.get(i); - String absoluteHref = link.attr("abs:href"); - this.proxyHttp(absoluteHref, filter); + for (Element link : document.select("a")) { + String absoluteHref = link.attr("abs:href"); + if (absoluteHref.startsWith(url)) { + proxyHttp(absoluteHref, filter); } } } catch (UnsupportedMimeTypeException e) { @@ -361,14 +358,14 @@ public class CaveRepositoryImpl extends CaveRepository { if (url.getProtocol().equals("file")) { // populate the Cave repository from a filesystem folder File populateFolder = new File(url.toURI()); - this.populateFromFilesystem(populateFolder, filter, update); + populateFromFilesystem(populateFolder, filter, update); } if (url.getProtocol().equals("http")) { // populate the Cave repository from a HTTP URL - this.populateFromHttp(url.toExternalForm(), filter, update); + populateFromHttp(url.toExternalForm(), filter, update); } if (update) { - this.generateRepositoryXml(); + generateRepositoryXml(); } } @@ -380,7 +377,7 @@ public class CaveRepositoryImpl extends CaveRepository { * @throws Exception */ public void populate(URL url, boolean update) throws Exception { - this.populate(url, null, update); + populate(url, null, update); } /** @@ -395,8 +392,10 @@ public class CaveRepositoryImpl extends CaveRepository { LOGGER.debug("Populating from filesystem {}", filesystem.getAbsolutePath()); if (filesystem.isDirectory()) { File[] children = filesystem.listFiles(); - for (int i = 0; i < children.length; i++) { - populateFromFilesystem(children[i], filter, update); + if (children != null) { + for (File child : children) { + populateFromFilesystem(child, filter, update); + } } } else { try { @@ -404,11 +403,11 @@ public class CaveRepositoryImpl extends CaveRepository { ResourceImpl resource = createResource(filesystem.toURI().toURL()); if (resource != null) { // copy the resource - File destination = new File(new File(this.getLocation()), filesystem.getName()); - LOGGER.debug("Copy from {} to {}", filesystem.getAbsolutePath(), destination.getAbsolutePath()); - FileUtils.copyFile(filesystem, destination); + Path destination = getLocationPath().resolve(filesystem.getName()); + LOGGER.debug("Copy from {} to {}", filesystem.getAbsolutePath(), destination.toAbsolutePath().toString()); + Files.copy(filesystem.toPath(), destination); if (update) { - resource = createResource(destination.toURI().toURL()); + resource = createResource(destination.toUri().toURL()); LOGGER.debug("Update the OBR metadata with {}-{}", ResolverUtil.getSymbolicName(resource), ResolverUtil.getVersion(resource)); addResource(resource); } @@ -430,16 +429,11 @@ public class CaveRepositoryImpl extends CaveRepository { */ private void populateFromHttp(String url, String filter, boolean update) throws Exception { LOGGER.debug("Populating from HTTP URL {}", url); - HttpClient httpClient = new DefaultHttpClient(); - - HttpGet httpGet = new HttpGet(url); - HttpResponse response = httpClient.execute(httpGet); - HttpEntity entity = response.getEntity(); - if (entity != null) { - if (entity.getContentType().getValue().equals("application/java-archive") - || entity.getContentType().getValue().equals("application/octet-stream")) { - // I have a jar/binary, potentially a resource + HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); + try (InputStream is = conn.getInputStream()) { + String type = conn.getContentType(); + if ("application/java-archive".equals(type) || "application/octet-stream".equals(type)) { try { if ((filter == null) || (url.matches(filter))) { ResourceImpl resource = createResource(new URL(url)); @@ -447,15 +441,12 @@ public class CaveRepositoryImpl extends CaveRepository { LOGGER.debug("Copy {} into the Cave repository storage", url); int index = url.lastIndexOf("/"); if (index > 0) { - url = url.substring(index); + url = url.substring(index + 1); } - File destination = new File(new File(getLocation()), url); - FileOutputStream outputStream = new FileOutputStream(destination); - entity.writeTo(outputStream); - outputStream.flush(); - outputStream.close(); + Path destination = getLocationPath().resolve(url); + Files.copy(is, destination); if (update) { - resource = createResource(destination.toURI().toURL()); + resource = createResource(destination.toUri().toURL()); LOGGER.debug("Update OBR metadata with {}-{}", ResolverUtil.getSymbolicName(resource), ResolverUtil.getVersion(resource)); addResource(resource); } @@ -466,13 +457,10 @@ public class CaveRepositoryImpl extends CaveRepository { } } else { // try to find link to "browse" - Document document = Jsoup.connect(url).get(); - - Elements links = document.select("a"); - if (links.size() > 1) { - for (int i = 1; i < links.size(); i++) { - Element link = links.get(i); - String absoluteHref = link.attr("abs:href"); + Document document = Jsoup.parse(is, "UTF-8", url); + for (Element link : document.select("a")) { + String absoluteHref = link.attr("abs:href"); + if (absoluteHref.startsWith(url)) { populateFromHttp(absoluteHref, filter, update); } } @@ -509,8 +497,8 @@ public class CaveRepositoryImpl extends CaveRepository { * @return the File corresponding to the OBR repository.xml. * @throws Exception */ - private File getRepositoryXmlFile() throws Exception { - return new File(new File(this.getLocation()), "repository.xml"); + private Path getRepositoryXmlFile() throws Exception { + return getLocationPath().resolve("repository.xml"); } public URL getResourceByUri(String uri) { @@ -538,8 +526,7 @@ public class CaveRepositoryImpl extends CaveRepository { * @throws Exception in case of lookup failure. */ public URL getRepositoryXml() throws Exception { - File repositoryXml = this.getRepositoryXmlFile(); - return repositoryXml.toURI().toURL(); + return getRepositoryXmlFile().toUri().toURL(); } /** @@ -548,7 +535,7 @@ public class CaveRepositoryImpl extends CaveRepository { * @throws Exception in case of destroy failure. */ public void cleanup() throws Exception { - FileUtils.deleteDirectory(new File(this.getLocation())); + Utils.deleteRecursive(getLocationPath()); } } http://git-wip-us.apache.org/repos/asf/karaf-cave/blob/a59c7a8e/server/storage/src/main/java/org/apache/karaf/cave/server/storage/OsgiRepository.java ---------------------------------------------------------------------- diff --git a/server/storage/src/main/java/org/apache/karaf/cave/server/storage/OsgiRepository.java b/server/storage/src/main/java/org/apache/karaf/cave/server/storage/OsgiRepository.java index 5d7f54d..e052487 100644 --- a/server/storage/src/main/java/org/apache/karaf/cave/server/storage/OsgiRepository.java +++ b/server/storage/src/main/java/org/apache/karaf/cave/server/storage/OsgiRepository.java @@ -1,7 +1,22 @@ +/* + * 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.server.storage; -import java.io.OutputStreamWriter; -import java.util.UUID; +import java.io.Writer; import javax.xml.stream.XMLStreamException; @@ -11,13 +26,14 @@ import org.osgi.resource.Resource; public class OsgiRepository extends XmlRepository { - StaxParser.XmlRepository repository; + OsgiLoader loader; public OsgiRepository(String url, String name) { this(url); - repository = new StaxParser.XmlRepository(); + StaxParser.XmlRepository repository = new StaxParser.XmlRepository(); repository.name = name; - getLoaders().put(url, new XmlLoader(url, repository)); + loader = new OsgiLoader(url, repository); + getLoaders().put(url, loader); } public OsgiRepository(String url) { @@ -26,22 +42,28 @@ public class OsgiRepository extends XmlRepository { public void addResource(Resource resource) { load(); - repository.resources.add(resource); - super.addResource(resource); + lock.writeLock().lock(); + try { + loader.getXml().resources.add(resource); + super.addResource(resource); + loader.getXml().increment = System.currentTimeMillis(); + } finally { + lock.writeLock().unlock(); + } } public long getIncrement() { load(); - return repository.increment; + return loader.getXml().increment; } public void setIncrement(long increment) { load(); - repository.increment = increment; + loader.getXml().increment = increment; } - public void writeRepository(OutputStreamWriter writer) throws XMLStreamException { - StaxParser.write(repository, writer); + public void writeRepository(Writer writer) throws XMLStreamException { + StaxParser.write(loader.getXml(), writer); } private void load() { @@ -49,4 +71,18 @@ public class OsgiRepository extends XmlRepository { getResources(); } + protected static class OsgiLoader extends XmlLoader { + public OsgiLoader(String url) { + super(url); + } + + public OsgiLoader(String url, StaxParser.XmlRepository xml) { + super(url, xml); + } + + public StaxParser.XmlRepository getXml() { + return xml; + } + } + } http://git-wip-us.apache.org/repos/asf/karaf-cave/blob/a59c7a8e/server/storage/src/main/java/org/apache/karaf/cave/server/storage/Utils.java ---------------------------------------------------------------------- diff --git a/server/storage/src/main/java/org/apache/karaf/cave/server/storage/Utils.java b/server/storage/src/main/java/org/apache/karaf/cave/server/storage/Utils.java new file mode 100644 index 0000000..8210266 --- /dev/null +++ b/server/storage/src/main/java/org/apache/karaf/cave/server/storage/Utils.java @@ -0,0 +1,45 @@ +/* + * 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.server.storage; + +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; + +public class Utils { + + public static void deleteRecursive(Path path) throws IOException { + 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; + } + }); + } + } + +} http://git-wip-us.apache.org/repos/asf/karaf-cave/blob/a59c7a8e/server/storage/src/main/resources/OSGI-INF/blueprint/cave-storage.xml ---------------------------------------------------------------------- diff --git a/server/storage/src/main/resources/OSGI-INF/blueprint/cave-storage.xml b/server/storage/src/main/resources/OSGI-INF/blueprint/cave-storage.xml index 554861d..54be010 100644 --- a/server/storage/src/main/resources/OSGI-INF/blueprint/cave-storage.xml +++ b/server/storage/src/main/resources/OSGI-INF/blueprint/cave-storage.xml @@ -18,15 +18,7 @@ --> <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0" - xmlns:cxf="http://cxf.apache.org/blueprint/core" - xmlns:jaxrs="http://cxf.apache.org/blueprint/jaxrs" - xsi:schemaLocation=" - http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd - http://cxf.apache.org/blueprint/jaxrs http://cxf.apache.org/schemas/blueprint/jaxrs.xsd - http://cxf.apache.org/blueprint/core http://cxf.apache.org/schemas/blueprint/core.xsd - " default-activation="lazy"> <bean id="caveRepositoryService" class="org.apache.karaf.cave.server.storage.CaveRepositoryServiceImpl" init-method="init"> @@ -43,11 +35,4 @@ </cm:default-properties> </cm:property-placeholder> - <!-- start the JAX-RS server --> - <jaxrs:server id="caveRepositoryJaxRsServer" address="/cave"> - <jaxrs:serviceBeans> - <ref component-id="caveRepositoryService"/> - </jaxrs:serviceBeans> - </jaxrs:server> - </blueprint> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/karaf-cave/blob/a59c7a8e/server/storage/src/test/java/org/apache/karaf/cave/server/storage/CaveRepositoryImplTest.java ---------------------------------------------------------------------- diff --git a/server/storage/src/test/java/org/apache/karaf/cave/server/storage/CaveRepositoryImplTest.java b/server/storage/src/test/java/org/apache/karaf/cave/server/storage/CaveRepositoryImplTest.java index 3eddf26..8908339 100644 --- a/server/storage/src/test/java/org/apache/karaf/cave/server/storage/CaveRepositoryImplTest.java +++ b/server/storage/src/test/java/org/apache/karaf/cave/server/storage/CaveRepositoryImplTest.java @@ -16,18 +16,18 @@ */ package org.apache.karaf.cave.server.storage; -import org.apache.commons.io.FileUtils; +import java.net.URL; +import java.nio.file.Paths; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import static org.apache.karaf.cave.server.storage.Utils.deleteRecursive; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import java.io.File; -import java.net.URL; - /** * Unit test of the Cave Repository Implementation. */ @@ -38,7 +38,7 @@ public class CaveRepositoryImplTest { @Before public void setUp() throws Exception { - FileUtils.deleteDirectory(new File("target/test-repository")); + deleteRecursive(Paths.get("target/test-repository")); repository = new CaveRepositoryImpl("test", "target/test-repository", false); }
