View Javadoc

1   package net.sourceforge.blogentis.slide;
2   
3   //-----------------------------------------------------------------------
4   //Blogentis - a blog publishing platform.
5   //Copyright (C) 2004 Tassos Bassoukos <abassouk@gmail.com>
6   //
7   //This library is free software; you can redistribute it and/or
8   //modify it under the terms of the GNU Lesser General Public
9   //License as published by the Free Software Foundation; either
10  //version 2.1 of the License, or (at your option) any later version.
11  //
12  //This library is distributed in the hope that it will be useful,
13  //but WITHOUT ANY WARRANTY; without even the implied warranty of
14  //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  //Lesser General Public License for more details.
16  //
17  //You should have received a copy of the GNU Lesser General Public
18  //License along with this library; if not, write to the Free Software
19  //Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  //-----------------------------------------------------------------------
21  //
22  //$Id: SlideService.java,v 1.4 2004/11/01 22:45:51 tassos Exp $
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             //rd.setOwner(token.getCredentialsToken().getPrincipal().getName());
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         //        req.setPathInfo(getSlidePath(b, smf.getPath()));
389         //        req.setContextPath(getSlidePath(b, smf.getPath()));
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 }