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