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