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