]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java
9d7deefb57200fd29b86d43bce0c4e3ff42138f5
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / internal / kernel / 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 static String httpRealm = "Argeo";
48
49 public NodeHttp() {
50 httpServiceTracker = new PrepareHttpStc();
51 // httpServiceTracker.open();
52 KernelUtils.asyncOpen(httpServiceTracker);
53 }
54
55 public void destroy() {
56 if (repositories != null)
57 repositories.close();
58 }
59
60 public static void registerRepositoryServlets(HttpService httpService, String alias, Repository repository) {
61 if (httpService == null)
62 throw new CmsException("No HTTP service available");
63 try {
64 registerWebdavServlet(httpService, alias, repository);
65 registerRemotingServlet(httpService, alias, repository);
66 if (NodeConstants.HOME.equals(alias))
67 registerFilesServlet(httpService, alias, repository);
68 if (log.isDebugEnabled())
69 log.debug("Registered servlets for repository '" + alias + "'");
70 } catch (Exception e) {
71 throw new CmsException("Could not register servlets for repository '" + alias + "'", e);
72 }
73 }
74
75 public static void unregisterRepositoryServlets(HttpService httpService, String alias) {
76 if (httpService == null)
77 return;
78 try {
79 httpService.unregister(webdavPath(alias));
80 httpService.unregister(remotingPath(alias));
81 if (NodeConstants.HOME.equals(alias))
82 httpService.unregister(filesPath(alias));
83 if (log.isDebugEnabled())
84 log.debug("Unregistered servlets for repository '" + alias + "'");
85 } catch (Exception e) {
86 log.error("Could not unregister servlets for repository '" + alias + "'", e);
87 }
88 }
89
90 static void registerWebdavServlet(HttpService httpService, String alias, Repository repository)
91 throws NamespaceException, ServletException {
92 // WebdavServlet webdavServlet = new WebdavServlet(repository, new
93 // OpenInViewSessionProvider(alias));
94 WebdavServlet webdavServlet = new WebdavServlet(repository, new CmsSessionProvider(alias));
95 String path = webdavPath(alias);
96 Properties ip = new Properties();
97 ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_CONFIG, HttpUtils.WEBDAV_CONFIG);
98 ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path);
99 httpService.registerServlet(path, webdavServlet, ip, new DataHttpContext(httpRealm));
100 }
101
102 static void registerFilesServlet(HttpService httpService, String alias, Repository repository)
103 throws NamespaceException, ServletException {
104 WebdavServlet filesServlet = new WebdavServlet(repository, new CmsSessionProvider(alias));
105 String path = filesPath(alias);
106 Properties ip = new Properties();
107 ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_CONFIG, HttpUtils.WEBDAV_CONFIG);
108 ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path);
109 httpService.registerServlet(path, filesServlet, ip, new PrivateHttpContext(httpRealm, true));
110 }
111
112 static void registerRemotingServlet(HttpService httpService, String alias, Repository repository)
113 throws NamespaceException, ServletException {
114 RemotingServlet remotingServlet = new RemotingServlet(repository, new CmsSessionProvider(alias));
115 String path = remotingPath(alias);
116 Properties ip = new Properties();
117 ip.setProperty(JcrRemotingServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path);
118 ip.setProperty(JcrRemotingServlet.INIT_PARAM_AUTHENTICATE_HEADER, "Negotiate");
119
120 // Looks like a bug in Jackrabbit remoting init
121 Path tmpDir;
122 try {
123 tmpDir = Files.createTempDirectory("remoting_" + alias);
124 } catch (IOException e) {
125 throw new CmsException("Cannot create temp directory for remoting servlet", e);
126 }
127 ip.setProperty(RemotingServlet.INIT_PARAM_HOME, tmpDir.toString());
128 ip.setProperty(RemotingServlet.INIT_PARAM_TMP_DIRECTORY, "remoting_" + alias);
129 ip.setProperty(RemotingServlet.INIT_PARAM_PROTECTED_HANDLERS_CONFIG, HttpUtils.DEFAULT_PROTECTED_HANDLERS);
130 ip.setProperty(RemotingServlet.INIT_PARAM_CREATE_ABSOLUTE_URI, "false");
131 httpService.registerServlet(path, remotingServlet, ip, new PrivateHttpContext(httpRealm));
132 }
133
134 static String webdavPath(String alias) {
135 return NodeConstants.PATH_DATA + "/" + alias;
136 }
137
138 static String remotingPath(String alias) {
139 return NodeConstants.PATH_JCR + "/" + alias;
140 }
141
142 static String filesPath(String alias) {
143 return NodeConstants.PATH_FILES;
144 }
145
146 // private Subject subjectFromRequest(HttpServletRequest request,
147 // HttpServletResponse response) {
148 // Authorization authorization = (Authorization)
149 // request.getAttribute(HttpContext.AUTHORIZATION);
150 // if (authorization == null)
151 // throw new CmsException("Not authenticated");
152 // try {
153 // LoginContext lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER,
154 // new HttpRequestCallbackHandler(request, response));
155 // lc.login();
156 // return lc.getSubject();
157 // } catch (LoginException e) {
158 // throw new CmsException("Cannot login", e);
159 // }
160 // }
161
162 static class RepositoriesStc extends ServiceTracker<Repository, Repository> {
163 private final HttpService httpService;
164
165 private final BundleContext bc;
166
167 public RepositoriesStc(BundleContext bc, HttpService httpService) {
168 super(bc, Repository.class, null);
169 this.httpService = httpService;
170 this.bc = bc;
171 }
172
173 @Override
174 public Repository addingService(ServiceReference<Repository> reference) {
175 Repository repository = bc.getService(reference);
176 Object jcrRepoAlias = reference.getProperty(NodeConstants.CN);
177 if (jcrRepoAlias != null) {
178 String alias = jcrRepoAlias.toString();
179 registerRepositoryServlets(httpService, alias, repository);
180 }
181 return repository;
182 }
183
184 @Override
185 public void modifiedService(ServiceReference<Repository> reference, Repository service) {
186 }
187
188 @Override
189 public void removedService(ServiceReference<Repository> reference, Repository service) {
190 Object jcrRepoAlias = reference.getProperty(NodeConstants.CN);
191 if (jcrRepoAlias != null) {
192 String alias = jcrRepoAlias.toString();
193 unregisterRepositoryServlets(httpService, alias);
194 }
195 }
196 }
197
198 private class PrepareHttpStc extends ServiceTracker<HttpService, HttpService> {
199 // private DataHttp dataHttp;
200 // private NodeHttp nodeHttp;
201
202 public PrepareHttpStc() {
203 super(bc, HttpService.class, null);
204 }
205
206 @Override
207 public HttpService addingService(ServiceReference<HttpService> reference) {
208 long begin = System.currentTimeMillis();
209 if (log.isTraceEnabled())
210 log.trace("HTTP prepare starts...");
211 HttpService httpService = addHttpService(reference);
212 if (log.isTraceEnabled())
213 log.trace("HTTP prepare duration: " + (System.currentTimeMillis() - begin) + "ms");
214 return httpService;
215 }
216
217 @Override
218 public void removedService(ServiceReference<HttpService> reference, HttpService service) {
219 // if (dataHttp != null)
220 // dataHttp.destroy();
221 // dataHttp = null;
222 // if (nodeHttp != null)
223 // nodeHttp.destroy();
224 // nodeHttp = null;
225 // destroy();
226 repositories.close();
227 repositories = null;
228 }
229
230 private HttpService addHttpService(ServiceReference<HttpService> sr) {
231 HttpService httpService = bc.getService(sr);
232 // TODO find constants
233 Object httpPort = sr.getProperty("http.port");
234 Object httpsPort = sr.getProperty("https.port");
235
236 try {
237 httpService.registerServlet("/!", new LinkServlet(), null, null);
238 httpService.registerServlet("/robots.txt", new RobotServlet(), null, null);
239 } catch (Exception e) {
240 throw new CmsException("Cannot register filters", e);
241 }
242 // track repositories
243 if (repositories != null)
244 throw new CmsException("An http service is already configured");
245 repositories = new RepositoriesStc(bc, httpService);
246 // repositories.open();
247 KernelUtils.asyncOpen(repositories);
248 log.info(httpPortsMsg(httpPort, httpsPort));
249 // httpAvailable = true;
250 // checkReadiness();
251
252 bc.registerService(NodeHttp.class, NodeHttp.this, null);
253 return httpService;
254 }
255
256 private String httpPortsMsg(Object httpPort, Object httpsPort) {
257 return "HTTP " + httpPort + (httpsPort != null ? " - HTTPS " + httpsPort : "");
258 }
259 }
260
261 private static class WebdavServlet extends SimpleWebdavServlet {
262 private static final long serialVersionUID = -4687354117811443881L;
263 private final Repository repository;
264
265 public WebdavServlet(Repository repository, SessionProvider sessionProvider) {
266 this.repository = repository;
267 setSessionProvider(sessionProvider);
268 }
269
270 public Repository getRepository() {
271 return repository;
272 }
273
274 @Override
275 protected void service(final HttpServletRequest request, final HttpServletResponse response)
276 throws ServletException, IOException {
277 WebdavServlet.super.service(request, response);
278 // try {
279 // Subject subject = subjectFromRequest(request);
280 // // TODO make it stronger, with eTags.
281 // // if (CurrentUser.isAnonymous(subject) &&
282 // // request.getMethod().equals("GET")) {
283 // // response.setHeader("Cache-Control", "no-transform, public,
284 // // max-age=300, s-maxage=900");
285 // // }
286 //
287 // Subject.doAs(subject, new PrivilegedExceptionAction<Void>() {
288 // @Override
289 // public Void run() throws Exception {
290 // WebdavServlet.super.service(request, response);
291 // return null;
292 // }
293 // });
294 // } catch (PrivilegedActionException e) {
295 // throw new CmsException("Cannot process webdav request",
296 // e.getException());
297 // }
298 }
299
300 }
301
302 private static class RemotingServlet extends JcrRemotingServlet {
303 private final Log log = LogFactory.getLog(RemotingServlet.class);
304 private static final long serialVersionUID = 4605238259548058883L;
305 private final Repository repository;
306 private final SessionProvider sessionProvider;
307
308 public RemotingServlet(Repository repository, SessionProvider sessionProvider) {
309 this.repository = repository;
310 this.sessionProvider = sessionProvider;
311 }
312
313 @Override
314 protected Repository getRepository() {
315 return repository;
316 }
317
318 @Override
319 protected SessionProvider getSessionProvider() {
320 return sessionProvider;
321 }
322
323 @Override
324 protected void service(final HttpServletRequest request, final HttpServletResponse response)
325 throws ServletException, IOException {
326 // try {
327 // Subject subject = subjectFromRequest(request, response);
328 // Subject.doAs(subject, new PrivilegedExceptionAction<Void>() {
329 // @Override
330 // public Void run() throws Exception {
331 if (log.isTraceEnabled())
332 HttpUtils.logRequest(log, request);
333 RemotingServlet.super.service(request, response);
334 // return null;
335 // }
336 // });
337 // } catch (PrivilegedActionException e) {
338 // throw new CmsException("Cannot process JCR remoting request",
339 // e.getException());
340 // }
341 }
342 }
343
344 }