Coverage Report - net.sourceforge.pebble.dao.file.FileStaticPageDAO
 
Classes in this File Line Coverage Branch Coverage Complexity
FileStaticPageDAO
37%
52/139
10%
3/30
2.875
FileStaticPageDAO$1
50%
1/2
0%
0/4
2.875
FileStaticPageDAO$StaticPageDateConverter
85%
23/27
50%
1/2
2.875
 
 1  
 /*
 2  
  * Copyright (c) 2003-2011, Simon Brown
 3  
  * All rights reserved.
 4  
  *
 5  
  * Redistribution and use in source and binary forms, with or without
 6  
  * modification, are permitted provided that the following conditions are met:
 7  
  *
 8  
  *   - Redistributions of source code must retain the above copyright
 9  
  *     notice, this list of conditions and the following disclaimer.
 10  
  *
 11  
  *   - Redistributions in binary form must reproduce the above copyright
 12  
  *     notice, this list of conditions and the following disclaimer in
 13  
  *     the documentation and/or other materials provided with the
 14  
  *     distribution.
 15  
  *
 16  
  *   - Neither the name of Pebble nor the names of its contributors may
 17  
  *     be used to endorse or promote products derived from this software
 18  
  *     without specific prior written permission.
 19  
  *
 20  
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 21  
  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 22  
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 23  
  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 24  
  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 25  
  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 26  
  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 27  
  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 28  
  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 29  
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 30  
  * POSSIBILITY OF SUCH DAMAGE.
 31  
  */
 32  
 
 33  
 package net.sourceforge.pebble.dao.file;
 34  
 
 35  
 import net.sourceforge.pebble.dao.PersistenceException;
 36  
 import net.sourceforge.pebble.dao.StaticPageDAO;
 37  
 import net.sourceforge.pebble.domain.Blog;
 38  
 import net.sourceforge.pebble.domain.StaticPage;
 39  
 import net.sourceforge.pebble.util.SecurityUtils;
 40  
 import org.apache.commons.logging.Log;
 41  
 import org.apache.commons.logging.LogFactory;
 42  
 
 43  
 import javax.xml.bind.JAXBContext;
 44  
 import javax.xml.bind.JAXBElement;
 45  
 import javax.xml.bind.Marshaller;
 46  
 import javax.xml.bind.Unmarshaller;
 47  
 import java.io.*;
 48  
 import java.text.SimpleDateFormat;
 49  
 import java.text.DateFormat;
 50  
 import java.text.ParseException;
 51  
 import java.util.*;
 52  
 
 53  0
 public class FileStaticPageDAO implements StaticPageDAO {
 54  
 
 55  
   /**
 56  
    * the log used by this class
 57  
    */
 58  4
   private static Log log = LogFactory.getLog(FileStaticPageDAO.class);
 59  
 
 60  
   private static final String STATIC_PAGES_DIRECTORY_NAME = "pages";
 61  
   private static final String STATIC_PAGE_FILE_EXTENSION = ".xml";
 62  
   private static final String STATIC_PAGE_LOCK_EXTENSION = ".lock";
 63  
 
 64  
   private JAXBContext jaxbContext;
 65  
 
 66  5024
   public FileStaticPageDAO() {
 67  
     try {
 68  5024
       jaxbContext = JAXBContext.newInstance(getClass().getPackage().getName());
 69  0
     } catch (Exception e) {
 70  0
       e.printStackTrace();
 71  5024
     }
 72  5024
   }
 73  
 
 74  
   /** the date/time format used when persisting dates */
 75  
   static final String OLD_PERSISTENT_DATETIME_FORMAT = "dd MMM yyyy HH:mm:ss z";
 76  
   static final String NEW_PERSISTENT_DATETIME_FORMAT = "dd MMM yyyy HH:mm:ss:S Z";
 77  
   static final String REGEX_FOR_YEAR = "\\d\\d\\d\\d";
 78  
 
 79  
   /**
 80  
    * Loads the static pages for a given blog.
 81  
    *
 82  
    * @param blog the owning Blog instance
 83  
    * @return a Collection of StaticPage instances
 84  
    * @throws net.sourceforge.pebble.dao.PersistenceException
 85  
    *          if static pages cannot be loaded
 86  
    */
 87  
   public Collection<StaticPage> loadStaticPages(Blog blog) throws PersistenceException {
 88  36
     List<StaticPage> list = new ArrayList<StaticPage>();
 89  36
     File root = new File(blog.getRoot(), STATIC_PAGES_DIRECTORY_NAME);
 90  36
     File files[] = root.listFiles(new FilenameFilter() {
 91  
         public boolean accept(File dir, String name) {
 92  0
           return new File(dir, name).isDirectory() && name.matches("\\d+");
 93  
         }
 94  
     });
 95  
     
 96  36
     if (files != null) {
 97  0
       for (File file : files) {
 98  0
         StaticPage staticPage = loadStaticPage(blog, file.getName());
 99  0
         if (staticPage != null) {
 100  0
           list.add(staticPage);
 101  
         }
 102  
       }
 103  
     }
 104  
 
 105  36
     return list;
 106  
   }
 107  
 
 108  
   /**
 109  
    * Loads a specific static page.
 110  
    *
 111  
    * @param blog   the owning Blog
 112  
    * @param pageId the page ID
 113  
    * @return a StaticPage instance
 114  
    * @throws net.sourceforge.pebble.dao.PersistenceException
 115  
    *          if the static page cannot be loaded
 116  
    */
 117  
   public StaticPage loadStaticPage(Blog blog, String pageId) throws PersistenceException {
 118  4
     File path = new File(getPath(blog, pageId));
 119  4
     File file = new File(path, pageId + STATIC_PAGE_FILE_EXTENSION);
 120  4
     return loadStaticPage(blog, file);
 121  
   }
 122  
 
 123  
   /**
 124  
    * Loads a static page from the specified file.
 125  
    *
 126  
    * @param blog      the Blog to which the static page belongs
 127  
    * @param source    the File pointing to the source
 128  
    * @return    a StaticPage instance
 129  
    * @throws net.sourceforge.pebble.dao.PersistenceException
 130  
    *          if the static page can't be loaded
 131  
    */
 132  
   private StaticPage loadStaticPage(Blog blog, File source) throws PersistenceException {
 133  4
     if (source.exists()) {
 134  4
       log.debug("Loading static page from " + source.getAbsolutePath());
 135  4
       StaticPage staticPage = new StaticPage(blog);
 136  
 
 137  
       try {
 138  4
         Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
 139  4
         JAXBElement<StaticPageType> controller = (JAXBElement)unmarshaller.unmarshal(source);
 140  4
         StaticPageType spt = controller.getValue();
 141  
 
 142  4
         staticPage.setTitle(spt.getTitle());
 143  4
         staticPage.setSubtitle(spt.getSubtitle());
 144  4
         staticPage.setBody(spt.getBody());
 145  4
         staticPage.setTags(spt.getTags());
 146  4
         staticPage.setAuthor(spt.getAuthor());
 147  4
         staticPage.setOriginalPermalink(spt.getOriginalPermalink());
 148  4
         staticPage.setName(spt.getStaticName());
 149  4
         StaticPageDateConverter converter = new StaticPageDateConverter(staticPage);
 150  4
         staticPage.setDate(converter.parse(spt.getDate()));
 151  4
         staticPage.setTemplate(spt.getTemplate());
 152  
 
 153  0
       } catch (Exception e) {
 154  0
         log.error(e.getMessage(), e);
 155  0
         e.printStackTrace();
 156  0
         throw new PersistenceException(e.getMessage());
 157  4
       }
 158  
 
 159  
       // and is the page locked?
 160  4
       staticPage.setLockedBy(getUsernameHoldingLock(staticPage));
 161  
 
 162  4
       return staticPage;
 163  
     } else {
 164  0
       return null;
 165  
     }
 166  
   }
 167  
 
 168  
   /**
 169  
    * Stores the specified static page.
 170  
    *
 171  
    * @param staticPage the static page to store
 172  
    * @throws net.sourceforge.pebble.dao.PersistenceException
 173  
    *          if something goes wrong storing the static page
 174  
    */
 175  
   public void storeStaticPage(StaticPage staticPage) throws PersistenceException {
 176  0
     File outputDir = new File(getPath(staticPage.getBlog(), staticPage.getId()));
 177  0
     if (!outputDir.exists()) {
 178  0
       outputDir.mkdirs();
 179  
     }
 180  
 
 181  0
     File outputFile = new File(outputDir, staticPage.getId() + STATIC_PAGE_FILE_EXTENSION);
 182  0
     storeStaticPage(staticPage, outputFile);
 183  0
   }
 184  
 
 185  
   /**
 186  
    * Stores a static page to the specified file.
 187  
    *
 188  
    * @param staticPage    the StaticPage that is being stored
 189  
    * @param destination the File pointing to the destination
 190  
    * @throws PersistenceException if something goes wrong storing the static page
 191  
    */
 192  
   private void storeStaticPage(StaticPage staticPage, File destination) throws PersistenceException {
 193  
     try {
 194  0
       Marshaller marshaller = jaxbContext.createMarshaller();
 195  0
       StaticPageType type = new StaticPageType();
 196  
 
 197  0
       type.setTitle(staticPage.getTitle());
 198  0
       type.setSubtitle(staticPage.getSubtitle());
 199  0
       type.setBody(staticPage.getBody());
 200  0
       type.setTags(staticPage.getTags());
 201  0
       type.setAuthor(staticPage.getAuthor());
 202  0
       type.setStaticName(staticPage.getName());
 203  0
       type.setOriginalPermalink(staticPage.getOriginalPermalink());
 204  0
       type.setState(ContentState.PUBLISHED);
 205  0
       type.setTitle(staticPage.getTitle());
 206  0
       type.setTemplate(staticPage.getTemplate());
 207  
 
 208  0
       StaticPageDateConverter converter = new StaticPageDateConverter(staticPage);
 209  0
       type.setDate(converter.format(staticPage.getDate()));
 210  
 
 211  
       // now take a backup of the correct file
 212  0
       if (destination.exists() && destination.length() > 0) {
 213  0
         removeStaticPage(staticPage); // this archives the current version
 214  
       }
 215  
 
 216  0
       log.debug("Saving to " + destination.getAbsolutePath());
 217  0
       ObjectFactory objectFactory = new ObjectFactory();
 218  0
       JAXBElement jaxbElement = objectFactory.createStaticPage(type);
 219  
 
 220  0
       marshaller.setProperty("jaxb.formatted.output", true);
 221  0
       marshaller.setProperty("jaxb.encoding", staticPage.getBlog().getCharacterEncoding());
 222  0
       FileWriter writer = new FileWriter(destination);
 223  0
       marshaller.marshal(jaxbElement, writer);
 224  0
       writer.flush();
 225  0
       writer.close();
 226  0
     } catch (Exception e) {
 227  0
       log.error(e.getMessage(), e);
 228  0
       e.printStackTrace();
 229  0
       throw new PersistenceException(e.getMessage());
 230  0
     }
 231  0
   }
 232  
 
 233  
   /**
 234  
    * Removes the specified static page.
 235  
    *
 236  
    * @param staticPage the static page to remove
 237  
    * @throws net.sourceforge.pebble.dao.PersistenceException
 238  
    *          if something goes wrong removing the page
 239  
    */
 240  
   public void removeStaticPage(StaticPage staticPage) throws PersistenceException {
 241  0
     File path = new File(getPath(staticPage.getBlog(), staticPage.getId()));
 242  0
     File file = new File(path, staticPage.getId() + STATIC_PAGE_FILE_EXTENSION);
 243  0
     log.debug("Removing " + staticPage.getGuid());
 244  
 
 245  
     // for archive purposes, the current version of the file will be saved with
 246  
     // the timestamp as the extension
 247  0
     SimpleDateFormat archiveFileExtension = new SimpleDateFormat("yyyyMMdd-HHmmss");
 248  0
     archiveFileExtension.setTimeZone(staticPage.getBlog().getTimeZone());
 249  0
     Date date = new Date();
 250  0
     if (file.exists()) {
 251  0
       date = new Date(file.lastModified());
 252  
     }
 253  0
     File backupFile = new File(
 254  
         file.getParentFile(),
 255  
         file.getName() + "." + archiveFileExtension.format(date));
 256  
 
 257  0
     if (backupFile.exists()) {
 258  0
       backupFile.delete();
 259  
     }
 260  
 
 261  0
     log.debug("Archiving current version to " + backupFile.getAbsolutePath());
 262  0
     boolean success = file.renameTo(backupFile);
 263  
 
 264  0
     if (!success) {
 265  0
       throw new PersistenceException("Deletion of " + staticPage.getGuid() + " failed");
 266  
     }
 267  0
   }
 268  
 
 269  
   /**
 270  
    * Given a blog and static page ID, this method determines the path where
 271  
    * that static page is stored.
 272  
    *
 273  
    * @param blog    the owning Blog
 274  
    * @param staticPageId   the ID of the static page
 275  
    * @return  a String of the form blogroot/yyyy/MM/dd
 276  
    */
 277  
   private String getPath(Blog blog, String staticPageId) {
 278  4
     StringBuffer buf = new StringBuffer();
 279  4
     buf.append(blog.getRoot());
 280  4
     buf.append(File.separator);
 281  4
     buf.append("pages");
 282  4
     buf.append(File.separator);
 283  4
     buf.append(staticPageId);
 284  
 
 285  4
     return buf.toString();
 286  
   }
 287  
 
 288  
   /**
 289  
    * Locks the specified static page.
 290  
    *
 291  
    * @param staticPage the static page to lock
 292  
    * @return  true if the page could be locked, false otherwise
 293  
    */
 294  
   public boolean lock(StaticPage staticPage) {
 295  0
     File lockFile = getLockFile(staticPage);
 296  
     try {
 297  0
       boolean success = lockFile.createNewFile();
 298  0
       if (success) {
 299  0
         FileWriter writer = new FileWriter(lockFile);
 300  0
         writer.write(SecurityUtils.getUsername());
 301  0
         writer.flush();
 302  0
         writer.close();
 303  0
         return true;
 304  
       } else {
 305  0
         String lockedBy = getUsernameHoldingLock(staticPage);
 306  0
         return (lockedBy != null && lockedBy.equals(SecurityUtils.getUsername()));
 307  
       }
 308  0
     } catch (IOException e) {
 309  0
       log.warn("Exceptoin while attempting to lock static page " + staticPage.getGuid(), e);
 310  
     }
 311  
 
 312  0
     return false;
 313  
   }
 314  
 
 315  
   /**
 316  
    * Unlocks the specified static page.
 317  
    *
 318  
    * @param staticPage the static page to unlock
 319  
    * @return true if the page could be unlocked, false otherwise
 320  
    */
 321  
   public boolean unlock(StaticPage staticPage) {
 322  0
     File lockFile = getLockFile(staticPage);
 323  0
     if (lockFile.exists()) {
 324  0
       return lockFile.delete();
 325  
     } else {
 326  0
       return true;
 327  
     }
 328  
   }
 329  
 
 330  
   /**
 331  
    * Given a static page, this method determines the path where
 332  
    * that static page is stored.
 333  
    *
 334  
    * @param staticPage    a StaticPage instance
 335  
    * @return  a File instance, representing a lock
 336  
    */
 337  
   private File getLockFile(StaticPage staticPage) {
 338  4
     StringBuffer buf = new StringBuffer();
 339  4
     buf.append(staticPage.getBlog().getRoot());
 340  4
     buf.append(File.separator);
 341  4
     buf.append(STATIC_PAGES_DIRECTORY_NAME);
 342  4
     buf.append(File.separator);
 343  4
     buf.append(staticPage.getId());
 344  4
     buf.append(STATIC_PAGE_LOCK_EXTENSION);
 345  
 
 346  4
     return new File(buf.toString());
 347  
   }
 348  
 
 349  
   private String getUsernameHoldingLock(StaticPage staticPage) {
 350  4
     String username = null;
 351  
     try {
 352  4
       File lockFile = getLockFile(staticPage);
 353  4
       if (lockFile.exists()) {
 354  0
         BufferedReader reader = new BufferedReader(new FileReader(lockFile));
 355  0
         username = reader.readLine();
 356  0
         reader.close();
 357  
       }
 358  0
     } catch (IOException ioe) {
 359  0
       log.warn("Error reading lock file", ioe);
 360  4
     }
 361  
 
 362  4
     return username;
 363  
   }
 364  
 
 365  
   class StaticPageDateConverter {
 366  
 
 367  
     private SimpleDateFormat dateTimeFormats[];
 368  
 
 369  4
     StaticPageDateConverter(StaticPage staticPage) {
 370  
       // create all date/time formats, for backwards compatibility
 371  
       SimpleDateFormat format;
 372  4
       dateTimeFormats = new SimpleDateFormat[6];
 373  
 
 374  4
       format = new SimpleDateFormat(FileBlogEntryDAO.NEW_PERSISTENT_DATETIME_FORMAT, Locale.ENGLISH);
 375  4
       format.setTimeZone(staticPage.getBlog().getTimeZone());
 376  4
       dateTimeFormats[0] = format;
 377  
 
 378  4
       format = new SimpleDateFormat(FileBlogEntryDAO.NEW_PERSISTENT_DATETIME_FORMAT, staticPage.getBlog().getLocale());
 379  4
       format.setTimeZone(staticPage.getBlog().getTimeZone());
 380  4
       dateTimeFormats[1] = format;
 381  
 
 382  4
       format = new SimpleDateFormat(FileBlogEntryDAO.NEW_PERSISTENT_DATETIME_FORMAT);
 383  4
       format.setTimeZone(staticPage.getBlog().getTimeZone());
 384  4
       dateTimeFormats[2] = format;
 385  
 
 386  4
       format = new SimpleDateFormat(FileBlogEntryDAO.OLD_PERSISTENT_DATETIME_FORMAT, Locale.ENGLISH);
 387  4
       format.setTimeZone(staticPage.getBlog().getTimeZone());
 388  4
       dateTimeFormats[3] = format;
 389  
 
 390  4
       format = new SimpleDateFormat(FileBlogEntryDAO.OLD_PERSISTENT_DATETIME_FORMAT, staticPage.getBlog().getLocale());
 391  4
       format.setTimeZone(staticPage.getBlog().getTimeZone());
 392  4
       dateTimeFormats[4] = format;
 393  
 
 394  4
       format = new SimpleDateFormat(FileBlogEntryDAO.OLD_PERSISTENT_DATETIME_FORMAT);
 395  4
       format.setTimeZone(staticPage.getBlog().getTimeZone());
 396  4
       dateTimeFormats[5] = format;
 397  4
     }
 398  
 
 399  
     Date parse(String s) {
 400  4
       for (DateFormat dateTimeFormat : dateTimeFormats) {
 401  
         try {
 402  4
           return dateTimeFormat.parse(s);
 403  0
         } catch (ParseException pe) {
 404  
           // do nothing, just try the next one
 405  
         }
 406  
       }
 407  
 
 408  0
       log.error("Could not parse date of " + s);
 409  0
       return null;
 410  
     }
 411  
 
 412  
     String format(Date date) {
 413  0
       return dateTimeFormats[0].format(date);
 414  
     }
 415  
 
 416  
   }
 417  
 
 418  
 }