1 package net.sourceforge.blogentis.slide;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 import java.io.FileNotFoundException;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.security.Principal;
29 import java.util.ArrayList;
30 import java.util.Date;
31 import java.util.HashSet;
32 import java.util.Iterator;
33 import java.util.List;
34 import java.util.Set;
35 import java.util.Vector;
36
37 import net.sourceforge.blogentis.om.Blog;
38 import net.sourceforge.blogentis.om.StoredBlog;
39 import net.sourceforge.blogentis.om.StoredBlogPeer;
40 import net.sourceforge.blogentis.storage.FileResourceFilter;
41 import net.sourceforge.blogentis.turbine.BlogRunData;
42 import net.sourceforge.blogentis.turbine.BlogRunDataService;
43 import net.sourceforge.blogentis.utils.BlogConstants;
44 import net.sourceforge.blogentis.utils.BlogManagerService;
45
46 import org.apache.commons.logging.Log;
47 import org.apache.commons.logging.LogFactory;
48 import org.apache.slide.authenticate.CredentialsToken;
49 import org.apache.slide.authenticate.SecurityToken;
50 import org.apache.slide.common.Domain;
51 import org.apache.slide.common.NamespaceAccessToken;
52 import org.apache.slide.common.SlideException;
53 import org.apache.slide.common.SlideToken;
54 import org.apache.slide.common.SlideTokenImpl;
55 import org.apache.slide.common.SlideTokenWrapper;
56 import org.apache.slide.content.NodeProperty;
57 import org.apache.slide.content.NodeRevisionContent;
58 import org.apache.slide.content.NodeRevisionDescriptor;
59 import org.apache.slide.content.NodeRevisionDescriptors;
60 import org.apache.slide.lock.ObjectLockedException;
61 import org.apache.slide.macro.DeleteMacroException;
62 import org.apache.slide.structure.ObjectAlreadyExistsException;
63 import org.apache.slide.structure.ObjectNode;
64 import org.apache.slide.structure.SubjectNode;
65 import org.apache.slide.webdav.WebdavException;
66 import org.apache.slide.webdav.method.MkcolMethod;
67 import org.apache.slide.webdav.method.PutMethod;
68 import org.apache.slide.webdav.util.WebdavConstants;
69 import org.apache.slide.webdav.util.WebdavStatus;
70 import org.apache.slide.webdav.util.WebdavUtils;
71 import org.apache.torque.TorqueException;
72 import org.apache.torque.util.Criteria;
73 import org.apache.turbine.om.security.User;
74 import org.apache.turbine.services.InitializationException;
75 import org.apache.turbine.services.TurbineBaseService;
76 import org.apache.turbine.services.TurbineServices;
77 import org.apache.turbine.services.rundata.DefaultTurbineRunData;
78 import org.apache.turbine.services.security.TurbineSecurity;
79 import org.apache.turbine.util.RunData;
80
81 /***
82 * @author abas
83 */
84 public class SlideService
85 extends TurbineBaseService {
86 private static final String INITIAL_BLOG_COLLECTIONS = "initialBlogCollections";
87 public static final String SLIDE_TOKEN_NAME = "net.sourceforge.blogentis.slide.SlideToken";
88 private static Log log = LogFactory.getLog(SlideService.class);
89
90 public static final String SERVICE_NAME = "SlideService";
91
92 private String namespace = null;
93
94 private NamespaceAccessToken accessToken = null;
95
96 private String blogStorePrefix = "";
97 public static final String BLOG_NAMESPACE = "BLOG:";
98
99 public void init()
100 throws InitializationException {
101 super.init();
102 namespace = getConfiguration().getString("namespace", "blogentis");
103 blogStorePrefix = configuration.getString("blogStorePrefix", "/blog");
104 accessToken = Domain
105 .accessNamespace(new SecurityToken(this), namespace);
106 try {
107 fixupBlogStores();
108 } catch (Exception e) {
109 throw new InitializationException(
110 "Could not ensure slide-blog mapping", e);
111 }
112 }
113
114 public void startRequest() {
115 try {
116 accessToken.begin();
117 } catch (Exception e) {
118 log.warn("Could not initiate transaction!", e);
119 }
120 }
121
122 public void endRequest() {
123 try {
124 accessToken.commit();
125 } catch (Exception e) {
126 log.error("Could not commit transaction!", e);
127 try {
128 accessToken.rollback();
129 } catch (Exception e2) {
130 log.error("Could not roll back transaction!", e2);
131 }
132 }
133 }
134
135 public void shutdown() {
136 Domain.closeNamespace(accessToken);
137 super.shutdown();
138 }
139
140 public static SlideService getInstance() {
141 return (SlideService)TurbineServices.getInstance()
142 .getService(SERVICE_NAME);
143 }
144
145 public Principal getPrincipal(RunData data) {
146 User user = DefaultTurbineRunData.getUserFromSession(data.getSession());
147 return (user != null) ? new TurbinePrincipal(user) : null;
148 }
149
150 public SlideToken getSlideToken(RunData data) {
151 SlideToken token = (SlideToken)data.getRequest()
152 .getAttribute(SLIDE_TOKEN_NAME);
153 if (token != null)
154 return token;
155 User u = data.getUser();
156 Principal p = TurbineSecurity.isAnonymousUser(u) ? null
157 : new TurbinePrincipal(u);
158 token = WebdavUtils
159 .getSlideToken(new MutablePrincipalHttpRequestWrapper(data
160 .getRequest(), p));
161 data.getRequest().setAttribute(SLIDE_TOKEN_NAME, token);
162 token.setExternalTx();
163 return token;
164 }
165
166 public SlideFileResource getFileIfExists(RunData data, Blog blog,
167 String path) {
168 if (blog == null)
169 return null;
170 SlideFileResource sfr = new SlideFileResource(data, blog, path);
171 if (!sfr.exists())
172 return null;
173 return sfr;
174 }
175
176 /***
177 * Map a (Blog,Path) pair to a Slide URI.
178 *
179 * @param blog
180 * the blog that this path refers to.
181 * @param path
182 * the original path;
183 * @param isInternal
184 * if true, returns the path directly mapping to a slide Node, if
185 * false returns the path as seen from an URI.
186 * @return the Slide URI that the path is mapped to.
187 */
188 public String getSlidePath(Blog blog, String path, boolean isInternal) {
189 StringBuffer sb;
190 if (isInternal)
191 sb = new StringBuffer(blogStorePrefix);
192 else
193 sb = new StringBuffer(100);
194 sb.append('/');
195 sb.append(blog.getName());
196 if ("".equals(path))
197 return sb.toString();
198 if (!path.startsWith("/"))
199 sb.append('/');
200 sb.append(path);
201 return sb.toString();
202 }
203
204 /***
205 * @return Returns the accessToken.
206 */
207 public NamespaceAccessToken getAccessToken() {
208 return accessToken;
209 }
210
211 /***
212 * Delete a file or directory from the slide store.
213 *
214 * @param data
215 * the RunData for the current request.
216 * @param b
217 * the Blog this file belongs to.
218 * @param path
219 * the file to delete
220 * @throws DeleteMacroException
221 * if the deletion failed.
222 */
223
224 public void deleteObject(RunData data, Blog b, String path)
225 throws DeleteMacroException {
226 SlideToken slideToken = getSlideToken(data);
227 try {
228 slideToken.setForceStoreEnlistment(true);
229 accessToken.getMacroHelper().delete(slideToken,
230 getSlidePath(b, path, true));
231 } catch (Exception e) {
232 throw new DeleteMacroException(e.toString());
233 }
234 }
235
236 public Set getAllChildren(RunData data, Blog b, String path,
237 FileResourceFilter filter) {
238 Set s = new HashSet();
239 String prefix = getSlidePath(b, "", true);
240 try {
241 ObjectNode node = accessToken.getStructureHelper()
242 .retrieve(getSlideToken(data), getSlidePath(b, path, true));
243 Vector v = node.getChildren();
244 for(Iterator i = v.iterator(); i.hasNext();) {
245 String uri = (String)i.next();
246 if (uri.startsWith(prefix)) {
247 String newPath = uri.substring(prefix.length());
248 if (filter != null && filter.ignored(newPath))
249 continue;
250 s.add(newPath);
251 if (filter == null || filter.descendInto(newPath))
252 s.addAll(getAllChildren(data, b, newPath, filter));
253 }
254 }
255 } catch (SlideException e) {}
256 return s;
257 }
258
259 private void fixupBlogStores()
260 throws TorqueException {
261 List l;
262 try {
263 l = StoredBlogPeer.doSelect(new Criteria());
264 } catch (TorqueException e) {
265 log.error("Could not get the list of blogs!", e);
266 return;
267 }
268 for(Iterator i = l.iterator(); i.hasNext();) {
269 StoredBlog blog = (StoredBlog)i.next();
270 fixupBlogStore(BlogManagerService.getBlog(blog.getBlogId()));
271 }
272 }
273
274 public void createCollection(RunData data, Blog b, String path)
275 throws Exception {
276 SlideToken slideToken = getSlideToken(data);
277 slideToken.setForceStoreEnlistment(true);
278 createCollection(accessToken, slideToken, b, path);
279 }
280
281 public void createCollection(NamespaceAccessToken nat, SlideToken token,
282 Blog b, String coll) {
283 SubjectNode node = new SubjectNode();
284 String uri = getSlidePath(b, coll, true);
285 String partName = uri.substring(uri.lastIndexOf('/') + 1);
286 try {
287 nat.getStructureHelper().create(token, node, uri);
288 NodeRevisionDescriptor rd = null;
289 NodeRevisionDescriptors rds = null;
290 try {
291 rds = accessToken.getContentHelper().retrieve(token, uri);
292 } catch (ObjectLockedException ignored) {}
293 if (rds != null && rds.hasRevisions()) {
294 try {
295 rd = accessToken.getContentHelper().retrieve(token, rds);
296 } catch (SlideException ignored) {}
297 }
298 if (rd == null) {
299 rd = new NodeRevisionDescriptor(0);
300 }
301 rd.setContentLength(0);
302 rd.setResourceType("<collection/>");
303 rd.setCreationDate(new Date());
304 rd.setModificationDate(new Date());
305 rd.setName(partName);
306 rd.setSource("");
307
308 rd.setProperty(new NodeProperty("iscollection", "1", "MICROSOFT"));
309 NodeRevisionContent rc = null;
310 if (rds != null && rds.hasRevisions()) {
311 try {
312 rc = accessToken.getContentHelper()
313 .retrieve(token, uri, rd);
314 } catch (SlideException ignored) {}
315 try {
316 accessToken.getContentHelper().store(token, uri, rd, rc);
317 } catch (SlideException ignored) {}
318 } else {
319 accessToken.getContentHelper().create(token, uri, rd, rc);
320 }
321 if (log.isDebugEnabled())
322 log.debug("Created collection " + uri);
323 } catch (ObjectAlreadyExistsException e) {
324 if (log.isDebugEnabled())
325 log.debug("Collection " + uri + " seems to exist");
326 } catch (SlideException e) {
327 if (log.isDebugEnabled())
328 log.debug("Could not create collection " + uri, e);
329 }
330 }
331
332 private SlideToken getAdminUser(Blog b) {
333 try {
334 List list = TurbineSecurity.getUserList(new Criteria());
335 for(Iterator i = list.iterator(); i.hasNext();) {
336 User u = (User)i.next();
337 if (TurbineSecurity.getACL(u)
338 .hasPermission(BlogConstants.PERM_ADMIN_BLOG, b.getName())) {
339 return new SlideTokenImpl(new CredentialsToken(
340 new TurbinePrincipal(u)));
341 }
342 }
343 } catch (Exception e) {
344 log.warn("Could not get Users for blog " + b.getName(), e);
345 }
346 log.warn("Could not get admin users for blog " + b.getName());
347 return new SlideTokenImpl(new CredentialsToken(""));
348 }
349
350 private void fixupBlogStore(Blog b) {
351 List collections = new ArrayList(20);
352 collections.add("");
353 collections
354 .addAll(getConfiguration().getList(INITIAL_BLOG_COLLECTIONS));
355 SlideTokenWrapper token = new SlideTokenWrapper(getAdminUser(b));
356 token.setForceSecurity(false);
357 token.setForceStoreEnlistment(true);
358 try {
359 accessToken.begin();
360 for(Iterator i = collections.iterator(); i.hasNext();)
361 createCollection(accessToken, token, b, (String)i.next());
362 accessToken.commit();
363 } catch (Exception e) {
364 log.error("Could not create blog structure for " + b.getName(), e);
365 try {
366 accessToken.rollback();
367 } catch (Exception e1) {
368 log.error("... and got an exception during rollback", e);
369 }
370 }
371 }
372
373 public boolean saveFile(Blog b, SlideMemoryFile smf) {
374 BlogRunData runData = BlogRunDataService.getCurrentRunData();
375 MutablePrincipalHttpRequestWrapper req = new MutablePrincipalHttpRequestWrapper(
376 runData.getRequest(), runData.getRequest().getUserPrincipal());
377 MemoryHttpServletResponse resp = new MemoryHttpServletResponse();
378 InputStream fis;
379 try {
380 fis = smf.getFileAsInputStream();
381 } catch (FileNotFoundException e) {
382 log.warn("Did not find object " + smf.getPath(), e);
383 return false;
384 }
385 req.setInputStream(fis);
386 req.setContentLength(smf.getSize());
387 req.setContentType(smf.getMimeType());
388
389
390 req.setServletPath(getSlidePath(b, smf.getPath(), false));
391 req.setMethod(WebdavConstants.M_PUT);
392
393 endRequest();
394 PutMethod p = new PutMethod(accessToken, BlogDavServlet
395 .getWebdavConfig());
396 try {
397 p.run(req, resp);
398 } catch (WebdavException e1) {
399 log.error("Could not save object at " + smf.getPath(), e1);
400 return false;
401 } finally {
402 try {
403 fis.close();
404 } catch (IOException e2) {
405 log.error("Could not close input stream for " + smf.getPath(),
406 e2);
407 }
408 startRequest();
409 }
410 if (resp.getStatusCode() != WebdavStatus.SC_CREATED
411 || resp.getStatusCode() != WebdavStatus.SC_NO_CONTENT)
412 return false;
413 return true;
414 }
415
416 public boolean makeCollection(Blog b, String path) {
417 BlogRunData runData = BlogRunDataService.getCurrentRunData();
418 MutablePrincipalHttpRequestWrapper req = new MutablePrincipalHttpRequestWrapper(
419 runData.getRequest(), runData.getRequest().getUserPrincipal());
420 MemoryHttpServletResponse resp = new MemoryHttpServletResponse();
421
422 req.setServletPath(getSlidePath(b, path, false));
423 req.setMethod(WebdavConstants.M_MKCOL);
424 req.setContentLength(0);
425
426 endRequest();
427 MkcolMethod p = new MkcolMethod(accessToken, BlogDavServlet
428 .getWebdavConfig());
429 try {
430 p.run(req, resp);
431 } catch (WebdavException e1) {
432 return false;
433 } finally {
434 startRequest();
435 }
436 if (resp.getStatusCode() != WebdavStatus.SC_CREATED
437 || resp.getStatusCode() != WebdavStatus.SC_NO_CONTENT)
438 return false;
439 return true;
440 }
441 }