]> git.argeo.org Git - lgpl/argeo-commons.git/blob - PkgServlet.java
ca4b6f74d70f9c27603fb7aaa9cefcc60f29bfde
[lgpl/argeo-commons.git] / PkgServlet.java
1 package org.argeo.cms.servlet.internal;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.net.FileNameMap;
6 import java.net.URL;
7 import java.net.URLConnection;
8 import java.util.Collection;
9 import java.util.SortedMap;
10 import java.util.TreeMap;
11
12 import javax.servlet.ServletException;
13 import javax.servlet.http.HttpServlet;
14 import javax.servlet.http.HttpServletRequest;
15 import javax.servlet.http.HttpServletResponse;
16
17 import org.argeo.cms.osgi.FilterRequirement;
18 import org.argeo.cms.osgi.PublishNamespace;
19 import org.argeo.cms.util.StreamUtils;
20 import org.osgi.framework.Bundle;
21 import org.osgi.framework.BundleContext;
22 import org.osgi.framework.FrameworkUtil;
23 import org.osgi.framework.Version;
24 import org.osgi.framework.VersionRange;
25 import org.osgi.framework.namespace.PackageNamespace;
26 import org.osgi.framework.wiring.BundleCapability;
27 import org.osgi.framework.wiring.BundleWiring;
28 import org.osgi.framework.wiring.FrameworkWiring;
29 import org.osgi.resource.Requirement;
30
31 /**
32 * Publishes client-side web resources (JavaScript, HTML, CSS, images, etc.)
33 * from the OSGi runtime.
34 */
35 public class PkgServlet extends HttpServlet {
36 private static final long serialVersionUID = 7660824185145214324L;
37
38 private static FileNameMap fileNameMap = URLConnection.getFileNameMap();
39
40 private BundleContext bundleContext = FrameworkUtil.getBundle(PkgServlet.class).getBundleContext();
41
42 @Override
43 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
44 String pathInfo = req.getPathInfo();
45
46 String pkg, versionStr, file;
47 String[] parts = pathInfo.split("/");
48 // first is always empty
49 if (parts.length == 4) {
50 pkg = parts[1];
51 versionStr = parts[2];
52 file = parts[3];
53 } else if (parts.length == 3) {
54 pkg = parts[1];
55 versionStr = null;
56 file = parts[2];
57 } else {
58 throw new IllegalArgumentException("Unsupported path length " + pathInfo);
59 }
60
61 // content type
62 String contentType = fileNameMap.getContentTypeFor(file);
63 resp.setContentType(contentType);
64
65 FrameworkWiring frameworkWiring = bundleContext.getBundle(0).adapt(FrameworkWiring.class);
66 String filter;
67 if (versionStr == null) {
68 filter = "(" + PackageNamespace.PACKAGE_NAMESPACE + "=" + pkg + ")";
69 } else {
70 if (versionStr.startsWith("[") || versionStr.startsWith("(")) {// range
71 VersionRange versionRange = new VersionRange(versionStr);
72 filter = "(&(" + PackageNamespace.PACKAGE_NAMESPACE + "=" + pkg + ")"
73 + versionRange.toFilterString(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE) + ")";
74
75 } else {
76 Version version = new Version(versionStr);
77 filter = "(&(" + PackageNamespace.PACKAGE_NAMESPACE + "=" + pkg + ")("
78 + PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE + "=" + version + "))";
79 }
80 }
81 Requirement requirement = new FilterRequirement(PackageNamespace.PACKAGE_NAMESPACE, filter);
82 Collection<BundleCapability> packages = frameworkWiring.findProviders(requirement);
83 if (packages.isEmpty()) {
84 resp.sendError(404);
85 return;
86 }
87
88 // TODO verify that it works with multiple versions
89 SortedMap<Version, BundleCapability> sorted = new TreeMap<>();
90 for (BundleCapability capability : packages) {
91 sorted.put(capability.getRevision().getVersion(), capability);
92 }
93
94 Bundle bundle = sorted.get(sorted.firstKey()).getRevision().getBundle();
95 String entryPath = '/' + pkg.replace('.', '/') + '/' + file;
96 URL internalURL = bundle.getResource(entryPath);
97 if (internalURL == null) {
98 resp.sendError(404);
99 return;
100 }
101
102 // Resource found, we now check whether it can be published
103 boolean publish = false;
104 BundleWiring bundleWiring = bundle.adapt(BundleWiring.class);
105 capabilities: for (BundleCapability bundleCapability : bundleWiring
106 .getCapabilities(PublishNamespace.CMS_PUBLISH_NAMESPACE)) {
107 Object publishedPkg = bundleCapability.getAttributes().get(PublishNamespace.PKG);
108 if (publishedPkg != null) {
109 if (publishedPkg.equals("*") || publishedPkg.equals(pkg)) {
110 Object publishedFile = bundleCapability.getAttributes().get(PublishNamespace.FILE);
111 if (publishedFile == null) {
112 publish = true;
113 break capabilities;
114 } else {
115 String[] publishedFiles = publishedFile.toString().split(",");
116 for (String pattern : publishedFiles) {
117 if (pattern.startsWith("*.")) {
118 String ext = pattern.substring(1);
119 if (file.endsWith(ext)) {
120 publish = true;
121 break capabilities;
122 }
123 } else {
124 if (publishedFile.equals(file)) {
125 publish = true;
126 break capabilities;
127 }
128 }
129 }
130 }
131 }
132 }
133 }
134
135 if (!publish) {
136 resp.sendError(404);
137 return;
138 }
139
140 try (InputStream in = internalURL.openStream()) {
141 StreamUtils.copy(in, resp.getOutputStream());
142 }
143 }
144
145 }