]> git.argeo.org Git - lgpl/argeo-commons.git/blob - NodeHttp.java
3e2f25146577eb82c8ec79fd98a4db9a6e732380
[lgpl/argeo-commons.git] / NodeHttp.java
1 package org.argeo.cms.internal.kernel;
2
3 import java.io.IOException;
4 import java.nio.file.Files;
5 import java.nio.file.Path;
6 import java.util.Properties;
7
8 import javax.jcr.Repository;
9 import javax.servlet.ServletException;
10 import javax.servlet.http.HttpServletRequest;
11 import javax.servlet.http.HttpServletResponse;
12
13 import org.apache.commons.logging.Log;
14 import org.apache.commons.logging.LogFactory;
15 import org.apache.jackrabbit.server.SessionProvider;
16 import org.apache.jackrabbit.server.remoting.davex.JcrRemotingServlet;
17 import org.apache.jackrabbit.webdav.simple.SimpleWebdavServlet;
18 import org.argeo.cms.CmsException;
19 import org.argeo.cms.internal.http.CmsSessionProvider;
20 import org.argeo.cms.internal.http.DataHttpContext;
21 import org.argeo.cms.internal.http.HttpUtils;
22 import org.argeo.cms.internal.http.LinkServlet;
23 import org.argeo.cms.internal.http.PrivateHttpContext;
24 import org.argeo.cms.internal.http.RobotServlet;
25 import org.argeo.node.NodeConstants;
26 import org.osgi.framework.BundleContext;
27 import org.osgi.framework.FrameworkUtil;
28 import org.osgi.framework.ServiceReference;
29 import org.osgi.service.http.HttpService;
30 import org.osgi.service.http.NamespaceException;
31 import org.osgi.util.tracker.ServiceTracker;
32
33 /**
34 * Intercepts and enriches http access, mainly focusing on security and
35 * transactionality.
36 */
37 public class NodeHttp implements KernelConstants {
38 private final static Log log = LogFactory.getLog(NodeHttp.class);
39
40 public final static String DEFAULT_SERVICE = "HTTP";
41
42 private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
43
44 private ServiceTracker<Repository, Repository> repositories;
45 private final ServiceTracker<HttpService, HttpService> httpServiceTracker;
46
47 private String httpRealm = "Argeo";
48 private String webDavConfig = HttpUtils.WEBDAV_CONFIG;
49 private final boolean cleanState;
50
51 public NodeHttp(boolean cleanState) {
52 this.cleanState = cleanState;
53 httpServiceTracker = new PrepareHttpStc();
54 // httpServiceTracker.open();
55 KernelUtils.asyncOpen(httpServiceTracker);
56 }
57
58 public void destroy() {
59 if (repositories != null)
60 repositories.close();
61 }
62
63 public void registerRepositoryServlets(HttpService httpService, String alias, Repository repository) {
64 if (httpService == null)
65 throw new CmsException("No HTTP service available");
66 try {
67 registerWebdavServlet(httpService, alias, repository);
68 registerRemotingServlet(httpService, alias, repository);
69 if (NodeConstants.HOME.equals(alias))
70 registerFilesServlet(httpService, alias, repository);
71 if (log.isTraceEnabled())
72 log.trace("Registered servlets for repository '" + alias + "'");
73 } catch (Exception e) {
74 throw new CmsException("Could not register servlets for repository '" + alias + "'", e);
75 }
76 }
77
78 public static void unregisterRepositoryServlets(HttpService httpService, String alias) {
79 if (httpService == null)
80 return;
81 try {
82 httpService.unregister(webdavPath(alias));
83 httpService.unregister(remotingPath(alias));
84 if (NodeConstants.HOME.equals(alias))
85 httpService.unregister(filesPath(alias));
86 if (log.isTraceEnabled())
87 log.trace("Unregistered servlets for repository '" + alias + "'");
88 } catch (Exception e) {
89 log.error("Could not unregister servlets for repository '" + alias + "'", e);
90 }
91 }
92
93 void registerWebdavServlet(HttpService httpService, String alias, Repository repository)
94 throws NamespaceException, ServletException {
95 // WebdavServlet webdavServlet = new WebdavServlet(repository, new
96 // OpenInViewSessionProvider(alias));
97 WebdavServlet webdavServlet = new WebdavServlet(repository, new CmsSessionProvider(alias));
98 String path = webdavPath(alias);
99 Properties ip = new Properties();
100 ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_CONFIG, webDavConfig);
101 ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path);
102 httpService.registerServlet(path, webdavServlet, ip, new DataHttpContext(httpRealm));
103 }
104
105 void registerFilesServlet(HttpService httpService, String alias, Repository repository)
106 throws NamespaceException, ServletException {
107 WebdavServlet filesServlet = new WebdavServlet(repository, new CmsSessionProvider(alias));
108 String path = filesPath(alias);
109 Properties ip = new Properties();
110 ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_CONFIG, webDavConfig);
111 ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path);
112 httpService.registerServlet(path, filesServlet, ip, new PrivateHttpContext(httpRealm, true));
113 }
114
115 void registerRemotingServlet(HttpService httpService, String alias, Repository repository)
116 throws NamespaceException, ServletException {
117 RemotingServlet remotingServlet = new RemotingServlet(repository, new CmsSessionProvider(alias));
118 String path = remotingPath(alias);
119 Properties ip = new Properties();
120 ip.setProperty(JcrRemotingServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path);
121 ip.setProperty(JcrRemotingServlet.INIT_PARAM_AUTHENTICATE_HEADER, "Negotiate");
122
123 // Looks like a bug in Jackrabbit remoting init
124 Path tmpDir;
125 try {
126 tmpDir = Files.createTempDirectory("remoting_" + alias);
127 } catch (IOException e) {
128 throw new CmsException("Cannot create temp directory for remoting servlet", e);
129 }
130 ip.setProperty(RemotingServlet.INIT_PARAM_HOME, tmpDir.toString());
131 ip.setProperty(RemotingServlet.INIT_PARAM_TMP_DIRECTORY, "remoting_" + alias);
132 ip.setProperty(RemotingServlet.INIT_PARAM_PROTECTED_HANDLERS_CONFIG, HttpUtils.DEFAULT_PROTECTED_HANDLERS);
133 ip.setProperty(RemotingServlet.INIT_PARAM_CREATE_ABSOLUTE_URI, "false");
134 httpService.registerServlet(path, remotingServlet, ip, new PrivateHttpContext(httpRealm));
135 }
136
137 static String webdavPath(String alias) {
138 return NodeConstants.PATH_DATA + "/" + alias;
139 }
140
141 static String remotingPath(String alias) {
142 return NodeConstants.PATH_JCR + "/" + alias;
143 }
144
145 static String filesPath(String alias) {
146 return NodeConstants.PATH_FILES;
147 }
148
149 class RepositoriesStc extends ServiceTracker<Repository, Repository> {
150 private final HttpService httpService;
151
152 private final BundleContext bc;
153
154 public RepositoriesStc(BundleContext bc, HttpService httpService) {
155 super(bc, Repository.class, null);
156 this.httpService = httpService;
157 this.bc = bc;
158 }
159
160 @Override
161 public Repository addingService(ServiceReference<Repository> reference) {
162 Repository repository = bc.getService(reference);
163 Object jcrRepoAlias = reference.getProperty(NodeConstants.CN);
164 if (jcrRepoAlias != null) {
165 String alias = jcrRepoAlias.toString();
166 registerRepositoryServlets(httpService, alias, repository);
167 }
168 return repository;
169 }
170
171 @Override
172 public void modifiedService(ServiceReference<Repository> reference, Repository service) {
173 }
174
175 @Override
176 public void removedService(ServiceReference<Repository> reference, Repository service) {
177 Object jcrRepoAlias = reference.getProperty(NodeConstants.CN);
178 if (jcrRepoAlias != null) {
179 String alias = jcrRepoAlias.toString();
180 unregisterRepositoryServlets(httpService, alias);
181 }
182 }
183 }
184
185 private class PrepareHttpStc extends ServiceTracker<HttpService, HttpService> {
186 public PrepareHttpStc() {
187 super(bc, HttpService.class, null);
188 }
189
190 @Override
191 public HttpService addingService(ServiceReference<HttpService> reference) {
192 long begin = System.currentTimeMillis();
193 if (log.isTraceEnabled())
194 log.trace("HTTP prepare starts...");
195 HttpService httpService = addHttpService(reference);
196 if (log.isTraceEnabled())
197 log.trace("HTTP prepare duration: " + (System.currentTimeMillis() - begin) + "ms");
198 return httpService;
199 }
200
201 @Override
202 public void removedService(ServiceReference<HttpService> reference, HttpService service) {
203 repositories.close();
204 repositories = null;
205 }
206
207 private HttpService addHttpService(ServiceReference<HttpService> sr) {
208 HttpService httpService = bc.getService(sr);
209 // TODO find constants
210 Object httpPort = sr.getProperty("http.port");
211 Object httpsPort = sr.getProperty("https.port");
212
213 try {
214 httpService.registerServlet("/!", new LinkServlet(), null, null);
215 httpService.registerServlet("/robots.txt", new RobotServlet(), null, null);
216 // httpService.registerServlet("/html", new HtmlServlet(), null, null);
217 } catch (Exception e) {
218 throw new CmsException("Cannot register filters", e);
219 }
220 // track repositories
221 if (repositories != null)
222 throw new CmsException("An http service is already configured");
223 repositories = new RepositoriesStc(bc, httpService);
224 // repositories.open();
225 if (cleanState)
226 KernelUtils.asyncOpen(repositories);
227 log.info(httpPortsMsg(httpPort, httpsPort));
228 // httpAvailable = true;
229 // checkReadiness();
230
231 bc.registerService(NodeHttp.class, NodeHttp.this, null);
232 return httpService;
233 }
234
235 private String httpPortsMsg(Object httpPort, Object httpsPort) {
236 return (httpPort != null ? "HTTP " + httpPort + " " : " ")
237 + (httpsPort != null ? "HTTPS " + httpsPort : "");
238 }
239 }
240
241 private static class WebdavServlet extends SimpleWebdavServlet {
242 private static final long serialVersionUID = -4687354117811443881L;
243 private final Repository repository;
244
245 public WebdavServlet(Repository repository, SessionProvider sessionProvider) {
246 this.repository = repository;
247 setSessionProvider(sessionProvider);
248 }
249
250 public Repository getRepository() {
251 return repository;
252 }
253
254 @Override
255 protected void service(final HttpServletRequest request, final HttpServletResponse response)
256 throws ServletException, IOException {
257 WebdavServlet.super.service(request, response);
258 // try {
259 // Subject subject = subjectFromRequest(request);
260 // // TODO make it stronger, with eTags.
261 // // if (CurrentUser.isAnonymous(subject) &&
262 // // request.getMethod().equals("GET")) {
263 // // response.setHeader("Cache-Control", "no-transform, public,
264 // // max-age=300, s-maxage=900");
265 // // }
266 //
267 // Subject.doAs(subject, new PrivilegedExceptionAction<Void>() {
268 // @Override
269 // public Void run() throws Exception {
270 // WebdavServlet.super.service(request, response);
271 // return null;
272 // }
273 // });
274 // } catch (PrivilegedActionException e) {
275 // throw new CmsException("Cannot process webdav request",
276 // e.getException());
277 // }
278 }
279
280 }
281
282 private static class RemotingServlet extends JcrRemotingServlet {
283 private final Log log = LogFactory.getLog(RemotingServlet.class);
284 private static final long serialVersionUID = 4605238259548058883L;
285 private final Repository repository;
286 private final SessionProvider sessionProvider;
287
288 public RemotingServlet(Repository repository, SessionProvider sessionProvider) {
289 this.repository = repository;
290 this.sessionProvider = sessionProvider;
291 }
292
293 @Override
294 protected Repository getRepository() {
295 return repository;
296 }
297
298 @Override
299 protected SessionProvider getSessionProvider() {
300 return sessionProvider;
301 }
302
303 @Override
304 protected void service(final HttpServletRequest request, final HttpServletResponse response)
305 throws ServletException, IOException {
306 if (log.isTraceEnabled())
307 HttpUtils.logRequest(log, request);
308 RemotingServlet.super.service(request, response);
309 }
310 }
311
312 }