]> git.argeo.org Git - lgpl/argeo-commons.git/blob - kernel/NodeHttp.java
Prepare next development cycle
[lgpl/argeo-commons.git] / kernel / NodeHttp.java
1 package org.argeo.cms.internal.kernel;
2
3 import java.io.IOException;
4 import java.nio.charset.Charset;
5 import java.nio.file.Files;
6 import java.nio.file.Path;
7 import java.util.Properties;
8
9 import javax.jcr.Repository;
10 import javax.servlet.ServletException;
11 import javax.servlet.http.HttpServletRequest;
12 import javax.servlet.http.HttpServletResponse;
13
14 import org.apache.commons.logging.Log;
15 import org.apache.commons.logging.LogFactory;
16 import org.apache.jackrabbit.server.SessionProvider;
17 import org.apache.jackrabbit.server.remoting.davex.JcrRemotingServlet;
18 import org.apache.jackrabbit.webdav.simple.SimpleWebdavServlet;
19 import org.argeo.cms.CmsException;
20 import org.argeo.cms.internal.http.CmsSessionProvider;
21 import org.argeo.cms.internal.http.DataHttpContext;
22 import org.argeo.cms.internal.http.HttpUtils;
23 import org.argeo.cms.internal.http.LinkServlet;
24 import org.argeo.cms.internal.http.PrivateHttpContext;
25 import org.argeo.cms.internal.http.RobotServlet;
26 import org.argeo.node.NodeConstants;
27 import org.osgi.framework.BundleContext;
28 import org.osgi.framework.FrameworkUtil;
29 import org.osgi.framework.ServiceReference;
30 import org.osgi.service.http.HttpService;
31 import org.osgi.service.http.NamespaceException;
32 import org.osgi.util.tracker.ServiceTracker;
33
34 /**
35 * Intercepts and enriches http access, mainly focusing on security and
36 * transactionality.
37 */
38 public class NodeHttp implements KernelConstants {
39 private final static Log log = LogFactory.getLog(NodeHttp.class);
40
41 public final static String DEFAULT_SERVICE = "HTTP";
42
43 private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
44
45 private ServiceTracker<Repository, Repository> repositories;
46 private final ServiceTracker<HttpService, HttpService> httpServiceTracker;
47
48 private static String httpRealm = "Argeo";
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 static 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.isDebugEnabled())
72 log.debug("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.isDebugEnabled())
87 log.debug("Unregistered servlets for repository '" + alias + "'");
88 } catch (Exception e) {
89 log.error("Could not unregister servlets for repository '" + alias + "'", e);
90 }
91 }
92
93 static 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, HttpUtils.WEBDAV_CONFIG);
101 ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path);
102 httpService.registerServlet(path, webdavServlet, ip, new DataHttpContext(httpRealm));
103 }
104
105 static 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, HttpUtils.WEBDAV_CONFIG);
111 ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path);
112 httpService.registerServlet(path, filesServlet, ip, new PrivateHttpContext(httpRealm, true));
113 }
114
115 static 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 // private Subject subjectFromRequest(HttpServletRequest request,
150 // HttpServletResponse response) {
151 // Authorization authorization = (Authorization)
152 // request.getAttribute(HttpContext.AUTHORIZATION);
153 // if (authorization == null)
154 // throw new CmsException("Not authenticated");
155 // try {
156 // LoginContext lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER,
157 // new HttpRequestCallbackHandler(request, response));
158 // lc.login();
159 // return lc.getSubject();
160 // } catch (LoginException e) {
161 // throw new CmsException("Cannot login", e);
162 // }
163 // }
164
165 static class RepositoriesStc extends ServiceTracker<Repository, Repository> {
166 private final HttpService httpService;
167
168 private final BundleContext bc;
169
170 public RepositoriesStc(BundleContext bc, HttpService httpService) {
171 super(bc, Repository.class, null);
172 this.httpService = httpService;
173 this.bc = bc;
174 }
175
176 @Override
177 public Repository addingService(ServiceReference<Repository> reference) {
178 Repository repository = bc.getService(reference);
179 Object jcrRepoAlias = reference.getProperty(NodeConstants.CN);
180 if (jcrRepoAlias != null) {
181 String alias = jcrRepoAlias.toString();
182 registerRepositoryServlets(httpService, alias, repository);
183 }
184 return repository;
185 }
186
187 @Override
188 public void modifiedService(ServiceReference<Repository> reference, Repository service) {
189 }
190
191 @Override
192 public void removedService(ServiceReference<Repository> reference, Repository service) {
193 Object jcrRepoAlias = reference.getProperty(NodeConstants.CN);
194 if (jcrRepoAlias != null) {
195 String alias = jcrRepoAlias.toString();
196 unregisterRepositoryServlets(httpService, alias);
197 }
198 }
199 }
200
201 private class PrepareHttpStc extends ServiceTracker<HttpService, HttpService> {
202 // private DataHttp dataHttp;
203 // private NodeHttp nodeHttp;
204
205 public PrepareHttpStc() {
206 super(bc, HttpService.class, null);
207 }
208
209 @Override
210 public HttpService addingService(ServiceReference<HttpService> reference) {
211 long begin = System.currentTimeMillis();
212 if (log.isTraceEnabled())
213 log.trace("HTTP prepare starts...");
214 HttpService httpService = addHttpService(reference);
215 if (log.isTraceEnabled())
216 log.trace("HTTP prepare duration: " + (System.currentTimeMillis() - begin) + "ms");
217 return httpService;
218 }
219
220 @Override
221 public void removedService(ServiceReference<HttpService> reference, HttpService service) {
222 // if (dataHttp != null)
223 // dataHttp.destroy();
224 // dataHttp = null;
225 // if (nodeHttp != null)
226 // nodeHttp.destroy();
227 // nodeHttp = null;
228 // destroy();
229 repositories.close();
230 repositories = null;
231 }
232
233 private HttpService addHttpService(ServiceReference<HttpService> sr) {
234 HttpService httpService = bc.getService(sr);
235 // TODO find constants
236 Object httpPort = sr.getProperty("http.port");
237 Object httpsPort = sr.getProperty("https.port");
238
239 try {
240 httpService.registerServlet("/!", new LinkServlet(), null, null);
241 httpService.registerServlet("/robots.txt", new RobotServlet(), null, null);
242 } catch (Exception e) {
243 throw new CmsException("Cannot register filters", e);
244 }
245 // track repositories
246 if (repositories != null)
247 throw new CmsException("An http service is already configured");
248 repositories = new RepositoriesStc(bc, httpService);
249 // repositories.open();
250 if (cleanState)
251 KernelUtils.asyncOpen(repositories);
252 log.info(httpPortsMsg(httpPort, httpsPort));
253 // httpAvailable = true;
254 // checkReadiness();
255
256 bc.registerService(NodeHttp.class, NodeHttp.this, null);
257 return httpService;
258 }
259
260 private String httpPortsMsg(Object httpPort, Object httpsPort) {
261 return "HTTP " + httpPort + (httpsPort != null ? " - HTTPS " + httpsPort : "");
262 }
263 }
264
265 private static class WebdavServlet extends SimpleWebdavServlet {
266 private static final long serialVersionUID = -4687354117811443881L;
267 private final Repository repository;
268
269 public WebdavServlet(Repository repository, SessionProvider sessionProvider) {
270 this.repository = repository;
271 setSessionProvider(sessionProvider);
272 }
273
274 public Repository getRepository() {
275 return repository;
276 }
277
278 @Override
279 protected void service(final HttpServletRequest request, final HttpServletResponse response)
280 throws ServletException, IOException {
281 WebdavServlet.super.service(request, response);
282 // try {
283 // Subject subject = subjectFromRequest(request);
284 // // TODO make it stronger, with eTags.
285 // // if (CurrentUser.isAnonymous(subject) &&
286 // // request.getMethod().equals("GET")) {
287 // // response.setHeader("Cache-Control", "no-transform, public,
288 // // max-age=300, s-maxage=900");
289 // // }
290 //
291 // Subject.doAs(subject, new PrivilegedExceptionAction<Void>() {
292 // @Override
293 // public Void run() throws Exception {
294 // WebdavServlet.super.service(request, response);
295 // return null;
296 // }
297 // });
298 // } catch (PrivilegedActionException e) {
299 // throw new CmsException("Cannot process webdav request",
300 // e.getException());
301 // }
302 }
303
304 }
305
306 private static class RemotingServlet extends JcrRemotingServlet {
307 private final Log log = LogFactory.getLog(RemotingServlet.class);
308 private static final long serialVersionUID = 4605238259548058883L;
309 private final Repository repository;
310 private final SessionProvider sessionProvider;
311
312 public RemotingServlet(Repository repository, SessionProvider sessionProvider) {
313 this.repository = repository;
314 this.sessionProvider = sessionProvider;
315 }
316
317 @Override
318 protected Repository getRepository() {
319 return repository;
320 }
321
322 @Override
323 protected SessionProvider getSessionProvider() {
324 return sessionProvider;
325 }
326
327 @Override
328 protected void service(final HttpServletRequest request, final HttpServletResponse response)
329 throws ServletException, IOException {
330 // request.setCharacterEncoding("US-ASCII");
331 System.out.println("## DEFAULT CHARSET: " + Charset.defaultCharset());
332 System.out.println("## Request CHARSET: " + request.getCharacterEncoding());
333
334 if (log.isTraceEnabled())
335 HttpUtils.logRequest(log, request);
336 RemotingServlet.super.service(request, response);
337 }
338 }
339
340 }