You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by kw...@apache.org on 2022/11/11 19:17:23 UTC
[sling-org-apache-sling-tooling-support-install] 01/01: SLING-11672 Migrate from Felix HTTP Whiteboard to OSGi Whiteboard
This is an automated email from the ASF dual-hosted git repository.
kwin pushed a commit to branch feature/osgi-http-whiteboard
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-tooling-support-install.git
commit a54ece9bfe6c3406e7a58551c223551fbc12f360
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Fri Nov 11 20:17:17 2022 +0100
SLING-11672 Migrate from Felix HTTP Whiteboard to OSGi Whiteboard
Update dependencies
Make refresh packages optional
---
pom.xml | 28 ++-
.../support/install/impl/InstallServlet.java | 233 +++++++++------------
.../support/install/impl/InstallationResult.java | 5 +-
3 files changed, 115 insertions(+), 151 deletions(-)
diff --git a/pom.xml b/pom.xml
index 87737b9..d169fb4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,7 +23,7 @@
<parent>
<groupId>org.apache.sling</groupId>
<artifactId>sling-bundle-parent</artifactId>
- <version>48</version>
+ <version>49</version>
</parent>
<artifactId>org.apache.sling.tooling.support.install</artifactId>
@@ -37,11 +37,12 @@
<url>https://gitbox.apache.org/repos/asf?p=sling-org-apache-sling-tooling-support-install.git</url>
<tag>HEAD</tag>
</scm>
-
+
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
@@ -50,7 +51,12 @@
</dependency>
<dependency>
<groupId>org.osgi</groupId>
- <artifactId>osgi.core</artifactId>
+ <artifactId>org.osgi.framework</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.service.http.whiteboard</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
@@ -64,22 +70,12 @@
<version>2.4</version>
<scope>provided</scope>
</dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-lang3</artifactId>
- <version>3.0</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>commons-fileupload</groupId>
- <artifactId>commons-fileupload</artifactId>
- <version>1.2.2</version>
- <scope>provided</scope>
- </dependency>
+
+ <!-- embedded dependency -->
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.utils</artifactId>
- <version>1.9.0</version>
+ <version>1.11.8</version>
<scope>provided</scope>
</dependency>
</dependencies>
diff --git a/src/main/java/org/apache/sling/tooling/support/install/impl/InstallServlet.java b/src/main/java/org/apache/sling/tooling/support/install/impl/InstallServlet.java
index 9501f9a..9c25c2e 100644
--- a/src/main/java/org/apache/sling/tooling/support/install/impl/InstallServlet.java
+++ b/src/main/java/org/apache/sling/tooling/support/install/impl/InstallServlet.java
@@ -16,17 +16,20 @@
*/
package org.apache.sling.tooling.support.install.impl;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
+import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.util.List;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collection;
+import java.util.Collections;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
+import java.util.stream.Stream;
import java.util.zip.Deflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@@ -36,31 +39,28 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.Part;
-import org.apache.commons.fileupload.FileItem;
-import org.apache.commons.fileupload.FileUploadException;
-import org.apache.commons.fileupload.disk.DiskFileItemFactory;
-import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.input.BoundedInputStream;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
+import org.osgi.framework.wiring.FrameworkWiring;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Reference;
-import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.service.http.whiteboard.propertytypes.HttpWhiteboardServletMultipart;
+import org.osgi.service.http.whiteboard.propertytypes.HttpWhiteboardServletPattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * Prototype for installing/updating a bundle from a directory
+ * ReST endpoint for installing/updating a bundle from a directory or a JAR
*/
-@Component(service = Servlet.class,
- property = {
- Constants.SERVICE_VENDOR + "=The Apache Software Foundation",
- "alias=/system/sling/tooling/install"
- })
+@Component(service = Servlet.class)
+@HttpWhiteboardServletPattern("/system/sling/tooling/install")
+@HttpWhiteboardServletMultipart(enabled = true, fileSizeThreshold = InstallServlet.UPLOAD_IN_MEMORY_SIZE_THRESHOLD)
public class InstallServlet extends HttpServlet {
private static final long serialVersionUID = -8820366266126231409L;
@@ -69,15 +69,13 @@ public class InstallServlet extends HttpServlet {
private static final String DIR = "dir";
- private static final int UPLOAD_IN_MEMORY_SIZE_THRESHOLD = 512 * 1024 * 1024;
+ public static final int UPLOAD_IN_MEMORY_SIZE_THRESHOLD = 512 * 1024 * 1024;
+ public static final int MANIFEST_SIZE_IN_INPUTSTREAM = 2 * 1024 * 1024;
- private BundleContext bundleContext;
-
- @Reference
- private PackageAdmin packageAdmin;
+ private final BundleContext bundleContext;
@Activate
- protected void activate(final BundleContext bundleContext) {
+ public InstallServlet(final BundleContext bundleContext) {
this.bundleContext = bundleContext;
}
@@ -85,9 +83,8 @@ public class InstallServlet extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
final String dirPath = req.getParameter(DIR);
-
- boolean isMultipart = ServletFileUpload.isMultipartContent(req);
-
+ final boolean refreshPackages = Boolean.parseBoolean(req.getParameter(dirPath));
+ boolean isMultipart = req.getContentType() != null && req.getContentType().toLowerCase().indexOf("multipart/form-data") > -1;
if (dirPath == null && !isMultipart) {
logger.error("No dir parameter specified : {} and no multipart content found", req.getParameterMap());
resp.setStatus(500);
@@ -96,77 +93,57 @@ public class InstallServlet extends HttpServlet {
result.render(resp.getWriter());
return;
}
-
if (isMultipart) {
- installBasedOnUploadedJar(req, resp);
+ installBasedOnUploadedJar(req, resp, refreshPackages);
} else {
- installBasedOnDirectory(resp, new File(dirPath));
+ installBasedOnDirectory(resp, Paths.get(dirPath), refreshPackages);
}
}
- private void installBasedOnUploadedJar(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ private void installBasedOnUploadedJar(HttpServletRequest req, HttpServletResponse resp, boolean refreshPackages) throws IOException, ServletException {
InstallationResult result = null;
- try {
- DiskFileItemFactory factory = new DiskFileItemFactory();
- // try to hold even largish bundles in memory to potentially improve performance
- factory.setSizeThreshold(UPLOAD_IN_MEMORY_SIZE_THRESHOLD);
-
- ServletFileUpload upload = new ServletFileUpload();
- upload.setFileItemFactory(factory);
-
- @SuppressWarnings("unchecked")
- List<FileItem> items = upload.parseRequest(req);
- if (items.size() != 1) {
- logAndWriteError("Found " + items.size() + " items to process, but only updating 1 bundle is supported", resp);
- return;
- }
+ Collection<Part> parts = req.getParts();
+ if (parts.size() != 1) {
+ logAndWriteError("Found " + parts.size() + " items to process, but only updating 1 bundle is supported", resp);
+ return;
+ }
- FileItem item = items.get(0);
+ Part part = parts.iterator().next();
- JarInputStream jar = null;
- InputStream rawInput = null;
- try {
- jar = new JarInputStream(item.getInputStream());
+ try (InputStream input = new BufferedInputStream(part.getInputStream(), MANIFEST_SIZE_IN_INPUTSTREAM)) {
+ input.mark(MANIFEST_SIZE_IN_INPUTSTREAM);
+ final String version;
+ final String symbolicName;
+ try (JarInputStream jar = new JarInputStream(new BoundedInputStream(input, MANIFEST_SIZE_IN_INPUTSTREAM))) {
+
Manifest manifest = jar.getManifest();
if (manifest == null) {
logAndWriteError("Uploaded jar file does not contain a manifest", resp);
return;
}
-
- final String symbolicName = manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
-
+ symbolicName = manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
if (symbolicName == null) {
logAndWriteError("Manifest does not have a " + Constants.BUNDLE_SYMBOLICNAME, resp);
return;
}
+ version = manifest.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
+ }
+ // go back to beginning of input stream
+ // the JarInputStream is used only for validation, we need a fresh input stream for updating
+ input.reset();
- final String version = manifest.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
-
- // the JarInputStream is used only for validation, we need a fresh input stream for updating
- rawInput = item.getInputStream();
-
- Bundle found = getBundle(symbolicName);
- try {
- installOrUpdateBundle(found, rawInput, "inputstream:" + symbolicName + "-" + version + ".jar");
+ Bundle found = getBundle(symbolicName);
+ try {
+ installOrUpdateBundle(found, input, "inputstream:" + symbolicName + "-" + version + ".jar", refreshPackages);
- result = new InstallationResult(true, null);
- resp.setStatus(200);
- result.render(resp.getWriter());
- return;
- } catch (BundleException e) {
- logAndWriteError("Unable to install/update bundle " + symbolicName, e, resp);
- return;
- }
- } finally {
- IOUtils.closeQuietly(jar);
- IOUtils.closeQuietly(rawInput);
+ result = new InstallationResult(true, null);
+ resp.setStatus(200);
+ result.render(resp.getWriter());
+ } catch (BundleException e) {
+ logAndWriteError("Unable to install/update bundle " + symbolicName, e, resp);
}
-
- } catch (FileUploadException e) {
- logAndWriteError("Failed parsing uploaded bundle", e, resp);
- return;
}
}
@@ -182,49 +159,39 @@ public class InstallServlet extends HttpServlet {
new InstallationResult(false, message + " : " + e.getMessage()).render(resp.getWriter());
}
- private void installBasedOnDirectory(HttpServletResponse resp, final File dir) throws FileNotFoundException,
- IOException {
-
+ private void installBasedOnDirectory(HttpServletResponse resp, final Path dir, boolean refreshPackages) throws IOException {
InstallationResult result = null;
- if ( dir.exists() && dir.isDirectory() ) {
+ if (Files.isDirectory(dir)) {
logger.info("Checking dir {} for bundle install", dir);
- final File manifestFile = new File(dir, JarFile.MANIFEST_NAME);
- if ( manifestFile.exists() ) {
- FileInputStream fis = null;
- try {
- fis = new FileInputStream(manifestFile);
+ final Path manifestFile = dir.resolve(JarFile.MANIFEST_NAME);
+ if (Files.exists(dir)) {
+ try (InputStream fis = Files.newInputStream(manifestFile)) {
final Manifest mf = new Manifest(fis);
final String symbolicName = mf.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
- if ( symbolicName != null ) {
+ if (symbolicName != null) {
// search bundle
Bundle found = getBundle(symbolicName);
- final File tempFile = File.createTempFile(dir.getName(), "bundle");
+ Path tmpJarFile = Files.createTempFile(dir.getFileName().toString(), "bundle");
try {
- createJar(dir, tempFile, mf);
-
- final InputStream in = new FileInputStream(tempFile);
- try {
- String location = dir.getAbsolutePath();
-
- installOrUpdateBundle(found, in, location);
+ createJar(dir, tmpJarFile, mf);
+ try(InputStream in = Files.newInputStream(tmpJarFile)) {
+ String location = dir.toAbsolutePath().toString();
+ installOrUpdateBundle(found, in, location, refreshPackages);
result = new InstallationResult(true, null);
resp.setStatus(200);
result.render(resp.getWriter());
- return;
- } catch ( final BundleException be ) {
+ } catch (final BundleException be) {
logAndWriteError("Unable to install/update bundle from dir " + dir, be, resp);
}
} finally {
- tempFile.delete();
+ Files.delete(tmpJarFile);
}
} else {
logAndWriteError("Manifest in " + dir + " does not have a symbolic name", resp);
}
- } finally {
- IOUtils.closeQuietly(fis);
}
} else {
result = new InstallationResult(false, "Dir " + dir + " does not have a manifest");
@@ -236,7 +203,7 @@ public class InstallServlet extends HttpServlet {
}
}
- private void installOrUpdateBundle(Bundle bundle, final InputStream in, String location) throws BundleException {
+ private void installOrUpdateBundle(Bundle bundle, final InputStream in, String location, boolean refreshPackages) throws BundleException {
if (bundle != null) {
// update
bundle.update(in);
@@ -245,10 +212,16 @@ public class InstallServlet extends HttpServlet {
final Bundle b = bundleContext.installBundle(location, in);
b.start();
}
+ if (refreshPackages) {
+ refreshBundle( bundle );
+ }
+ }
+ private void refreshBundle(Bundle bundle) {
+ FrameworkWiring frameworkWiring = bundleContext.getBundle(Constants.SYSTEM_BUNDLE_ID).adapt(FrameworkWiring.class);
// take into account added/removed packages for updated bundles and newly satisfied optional package imports
// for new installed bundles
- packageAdmin.refreshPackages(new Bundle[] { bundle });
+ frameworkWiring.refreshBundles(Collections.singleton(bundle));
}
private Bundle getBundle(final String symbolicName) {
@@ -262,10 +235,8 @@ public class InstallServlet extends HttpServlet {
return found;
}
- private static void createJar(final File sourceDir, final File jarFile, final Manifest mf)
- throws IOException {
- final JarOutputStream zos = new JarOutputStream(new FileOutputStream(jarFile));
- try {
+ private static void createJar(final Path sourceDir, final Path jarFile, final Manifest mf) throws IOException {
+ try (JarOutputStream zos = new JarOutputStream(Files.newOutputStream(jarFile))) {
zos.setLevel(Deflater.NO_COMPRESSION);
// manifest first
final ZipEntry anEntry = new ZipEntry(JarFile.MANIFEST_NAME);
@@ -273,38 +244,36 @@ public class InstallServlet extends HttpServlet {
mf.write(zos);
zos.closeEntry();
zipDir(sourceDir, zos, "");
- } finally {
- try {
- zos.close();
- } catch ( final IOException ignore ) {
- // ignore
- }
}
}
- public static void zipDir(final File sourceDir, final ZipOutputStream zos, final String path)
- throws IOException {
- final byte[] readBuffer = new byte[8192];
- int bytesIn = 0;
+ public static void zipDir(final Path sourceDir, final ZipOutputStream zos, final String prefix) throws IOException {
+ try (Stream<Path> stream = Files.list(sourceDir)) {
+ stream.forEach(p ->
+ {
+ try {
+ zipFileOrDir(p, zos, prefix);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ });
+ } catch (UncheckedIOException ioe) {
+ throw ioe.getCause();
+ }
+ }
- for(final File f : sourceDir.listFiles()) {
- if (f.isDirectory()) {
- final String prefix = path + f.getName() + "/";
- zos.putNextEntry(new ZipEntry(prefix));
- zipDir(f, zos, prefix);
- } else {
- final String entry = path + f.getName();
- if ( !JarFile.MANIFEST_NAME.equals(entry) ) {
- final FileInputStream fis = new FileInputStream(f);
- try {
- final ZipEntry anEntry = new ZipEntry(entry);
- zos.putNextEntry(anEntry);
- while ( (bytesIn = fis.read(readBuffer)) != -1) {
- zos.write(readBuffer, 0, bytesIn);
- }
- } finally {
- fis.close();
- }
+ private static void zipFileOrDir(final Path sourceFileOrDir, final ZipOutputStream zos, final String prefix) throws IOException {
+ if (Files.isDirectory(sourceFileOrDir)) {
+ final String newPrefix = prefix + sourceFileOrDir.getFileName() + "/";
+ zos.putNextEntry(new ZipEntry(newPrefix));
+ zipDir(sourceFileOrDir, zos, newPrefix);
+ } else {
+ final String entry = prefix + sourceFileOrDir.getFileName();
+ if (!JarFile.MANIFEST_NAME.equals(entry)) {
+ try (InputStream fis = Files.newInputStream(sourceFileOrDir)) {
+ final ZipEntry anEntry = new ZipEntry(entry);
+ zos.putNextEntry(anEntry);
+ IOUtils.copy(fis, zos);
}
}
}
diff --git a/src/main/java/org/apache/sling/tooling/support/install/impl/InstallationResult.java b/src/main/java/org/apache/sling/tooling/support/install/impl/InstallationResult.java
index 25f9924..d97894f 100644
--- a/src/main/java/org/apache/sling/tooling/support/install/impl/InstallationResult.java
+++ b/src/main/java/org/apache/sling/tooling/support/install/impl/InstallationResult.java
@@ -19,13 +19,12 @@ package org.apache.sling.tooling.support.install.impl;
import java.io.IOException;
import java.io.Writer;
-import org.apache.commons.lang3.StringUtils;
import org.apache.felix.utils.json.JSONWriter;
public class InstallationResult {
private final boolean status;
- private final String message;
+ private final String message; // may be null
public InstallationResult(boolean status, String message) {
this.status = status;
@@ -38,7 +37,7 @@ public class InstallationResult {
JSONWriter writer = new JSONWriter(out);
writer.object();
writer.key("status").value(status ? "OK" : "FAILURE");
- if (!StringUtils.isEmpty(message)) {
+ if (message != null) {
writer.key("message").value(message);
}
writer.endObject();