View Javadoc
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.api.NodeConstants;
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.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  @Deprecated
38  public class NodeHttp implements KernelConstants {
39  	private final static Log log = LogFactory.getLog(NodeHttp.class);
40  
41  	private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
42  
43  	private ServiceTracker<Repository, Repository> repositories;
44  	private final ServiceTracker<HttpService, HttpService> httpServiceTracker;
45  
46  	private String httpRealm = "Argeo";
47  	private String webDavConfig = HttpUtils.WEBDAV_CONFIG;
48  //	private final boolean cleanState;
49  
50  	public NodeHttp() {
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 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.EGO_REPOSITORY.equals(alias))
69  				registerFilesServlet(httpService, alias, repository);
70  			if (log.isTraceEnabled())
71  				log.trace("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.EGO_REPOSITORY.equals(alias))
84  				httpService.unregister(filesPath(alias));
85  			if (log.isTraceEnabled())
86  				log.trace("Unregistered servlets for repository '" + alias + "'");
87  		} catch (Exception e) {
88  			log.error("Could not unregister servlets for repository '" + alias + "'", e);
89  		}
90  	}
91  
92  	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, webDavConfig);
100 		ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path);
101 		httpService.registerServlet(path, webdavServlet, ip, new DataHttpContext(httpRealm));
102 	}
103 
104 	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, webDavConfig);
110 		ip.setProperty(WebdavServlet.INIT_PARAM_RESOURCE_PATH_PREFIX, path);
111 		httpService.registerServlet(path, filesServlet, ip, new PrivateHttpContext(httpRealm, true));
112 	}
113 
114 	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 	class RepositoriesStc extends ServiceTracker<Repository, Repository> {
149 		private final HttpService httpService;
150 
151 		private final BundleContext bc;
152 
153 		public RepositoriesStc(BundleContext bc, HttpService httpService) {
154 			super(bc, Repository.class, null);
155 			this.httpService = httpService;
156 			this.bc = bc;
157 		}
158 
159 		@Override
160 		public Repository addingService(ServiceReference<Repository> reference) {
161 			Repository repository = bc.getService(reference);
162 			Object jcrRepoAlias = reference.getProperty(NodeConstants.CN);
163 			if (jcrRepoAlias != null) {
164 				String alias = jcrRepoAlias.toString();
165 				registerRepositoryServlets(httpService, alias, repository);
166 			}
167 			return repository;
168 		}
169 
170 		@Override
171 		public void modifiedService(ServiceReference<Repository> reference, Repository service) {
172 		}
173 
174 		@Override
175 		public void removedService(ServiceReference<Repository> reference, Repository service) {
176 			Object jcrRepoAlias = reference.getProperty(NodeConstants.CN);
177 			if (jcrRepoAlias != null) {
178 				String alias = jcrRepoAlias.toString();
179 				unregisterRepositoryServlets(httpService, alias);
180 			}
181 		}
182 	}
183 
184 	private class PrepareHttpStc extends ServiceTracker<HttpService, HttpService> {
185 		public PrepareHttpStc() {
186 			super(bc, HttpService.class, null);
187 		}
188 
189 		@Override
190 		public HttpService addingService(ServiceReference<HttpService> reference) {
191 			long begin = System.currentTimeMillis();
192 			if (log.isTraceEnabled())
193 				log.trace("HTTP prepare starts...");
194 			HttpService httpService = addHttpService(reference);
195 			if (log.isTraceEnabled())
196 				log.trace("HTTP prepare duration: " + (System.currentTimeMillis() - begin) + "ms");
197 			return httpService;
198 		}
199 
200 		@Override
201 		public void removedService(ServiceReference<HttpService> reference, HttpService service) {
202 			repositories.close();
203 			repositories = null;
204 		}
205 
206 		private HttpService addHttpService(ServiceReference<HttpService> sr) {
207 			HttpService httpService = bc.getService(sr);
208 			// TODO find constants
209 			Object httpPort = sr.getProperty("http.port");
210 			Object httpsPort = sr.getProperty("https.port");
211 
212 			try {
213 				httpService.registerServlet("/!", new LinkServlet(), null, null);
214 				httpService.registerServlet("/robots.txt", new RobotServlet(), null, null);
215 				// httpService.registerServlet("/html", new HtmlServlet(), null, null);
216 			} catch (Exception e) {
217 				throw new CmsException("Cannot register filters", e);
218 			}
219 			// track repositories
220 			if (repositories != null)
221 				throw new CmsException("An http service is already configured");
222 			repositories = new RepositoriesStc(bc, httpService);
223 			// repositories.open();
224 
225 			///if (cleanState)
226 			// FIXME properly publish servlets
227 			//KernelUtils.asyncOpen(repositories);
228 
229 			log.info(httpPortsMsg(httpPort, httpsPort));
230 			// httpAvailable = true;
231 			// checkReadiness();
232 
233 			bc.registerService(NodeHttp.class, NodeHttp.this, null);
234 			return httpService;
235 		}
236 
237 		private String httpPortsMsg(Object httpPort, Object httpsPort) {
238 			return (httpPort != null ? "HTTP " + httpPort + " " : " ")
239 					+ (httpsPort != null ? "HTTPS " + httpsPort : "");
240 		}
241 	}
242 
243 	private static class WebdavServlet extends SimpleWebdavServlet {
244 		private static final long serialVersionUID = -4687354117811443881L;
245 		private final Repository repository;
246 
247 		public WebdavServlet(Repository repository, SessionProvider sessionProvider) {
248 			this.repository = repository;
249 			setSessionProvider(sessionProvider);
250 		}
251 
252 		public Repository getRepository() {
253 			return repository;
254 		}
255 
256 		@Override
257 		protected void service(final HttpServletRequest request, final HttpServletResponse response)
258 				throws ServletException, IOException {
259 			WebdavServlet.super.service(request, response);
260 			// try {
261 			// Subject subject = subjectFromRequest(request);
262 			// // TODO make it stronger, with eTags.
263 			// // if (CurrentUser.isAnonymous(subject) &&
264 			// // request.getMethod().equals("GET")) {
265 			// // response.setHeader("Cache-Control", "no-transform, public,
266 			// // max-age=300, s-maxage=900");
267 			// // }
268 			//
269 			// Subject.doAs(subject, new PrivilegedExceptionAction<Void>() {
270 			// @Override
271 			// public Void run() throws Exception {
272 			// WebdavServlet.super.service(request, response);
273 			// return null;
274 			// }
275 			// });
276 			// } catch (PrivilegedActionException e) {
277 			// throw new CmsException("Cannot process webdav request",
278 			// e.getException());
279 			// }
280 		}
281 
282 	}
283 
284 	private static class RemotingServlet extends JcrRemotingServlet {
285 		private final Log log = LogFactory.getLog(RemotingServlet.class);
286 		private static final long serialVersionUID = 4605238259548058883L;
287 		private final Repository repository;
288 		private final SessionProvider sessionProvider;
289 
290 		public RemotingServlet(Repository repository, SessionProvider sessionProvider) {
291 			this.repository = repository;
292 			this.sessionProvider = sessionProvider;
293 		}
294 
295 		@Override
296 		protected Repository getRepository() {
297 			return repository;
298 		}
299 
300 		@Override
301 		protected SessionProvider getSessionProvider() {
302 			return sessionProvider;
303 		}
304 
305 		@Override
306 		protected void service(final HttpServletRequest request, final HttpServletResponse response)
307 				throws ServletException, IOException {
308 			if (log.isTraceEnabled())
309 				HttpUtils.logRequest(log, request);
310 			RemotingServlet.super.service(request, response);
311 		}
312 	}
313 
314 }