Coverage Report - net.sourceforge.pebble.domain.Theme
 
Classes in this File Line Coverage Branch Coverage Complexity
Theme
68%
46/67
72%
13/18
1.917
 
 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  
 package net.sourceforge.pebble.domain;
 33  
 
 34  
 import net.sourceforge.pebble.util.FileUtils;
 35  
 import org.apache.commons.io.IOUtils;
 36  
 import org.apache.commons.logging.Log;
 37  
 import org.apache.commons.logging.LogFactory;
 38  
 
 39  
 import java.io.File;
 40  
 import java.io.FileInputStream;
 41  
 import java.io.FileOutputStream;
 42  
 import java.io.IOException;
 43  
 import java.nio.channels.FileChannel;
 44  
 
 45  
 /**
 46  
  * Represents the user's editable theme.
 47  
  *
 48  
  * @author Simon Brown
 49  
  */
 50  
 public class Theme {
 51  
 
 52  
   /**
 53  
    * the log used by this class
 54  
    */
 55  4
   private static Log log = LogFactory.getLog(Theme.class);
 56  
 
 57  
   /**
 58  
    * the name of the theme that should be used as a default
 59  
    */
 60  
   public static final String DEFAULT_THEME_NAME = "default";
 61  
 
 62  
   /**
 63  
    * the blog to which this theme belongs
 64  
    */
 65  
   private Blog blog;
 66  
 
 67  
   /**
 68  
    * the name of the theme
 69  
    */
 70  
   private String name;
 71  
 
 72  
   /**
 73  
    * the path of the live theme (under the webapp root)
 74  
    */
 75  
   private String pathToLiveThemes;
 76  
 
 77  
   /**
 78  
    * Creates a new Theme instance with the specified details.
 79  
    *
 80  
    * @param blog             the owning Blog instance
 81  
    * @param name             the name of the theme
 82  
    * @param pathToLiveThemes the path to the live themes
 83  
    */
 84  2716
   public Theme(Blog blog, String name, String pathToLiveThemes) {
 85  2716
     this.blog = blog;
 86  2716
     this.name = name;
 87  2716
     this.pathToLiveThemes = pathToLiveThemes;
 88  2716
   }
 89  
 
 90  
   /**
 91  
    * Gets the location where the backup version of the blog theme is stored -
 92  
    * under the blog.dir directory, in a sub-directory called "theme".
 93  
    *
 94  
    * @return an absolute, local path on the filing system
 95  
    */
 96  
   String getBackupThemeDirectory() {
 97  21552
     return blog.getRoot() + File.separator + "theme";
 98  
   }
 99  
 
 100  
   public File getPathToLiveTheme() {
 101  2716
     return new File(pathToLiveThemes, name);
 102  
   }
 103  
 
 104  
   /**
 105  
    * Restores the theme from the blog.dir to the webapp.
 106  
    */
 107  
   public void restore() {
 108  2692
     restore(DEFAULT_THEME_NAME);
 109  2692
   }
 110  
 
 111  
   /**
 112  
    * Restores the theme from the blog.dir to the webapp.
 113  
    */
 114  
   public void restore(String themeName) {
 115  2692
     File blogTheme = new File(getBackupThemeDirectory());
 116  2692
     if (!blogTheme.exists() || blogTheme.listFiles().length == 0) {
 117  2692
       copy(themeName);
 118  
     }
 119  
 
 120  2692
     log.debug("Restoring " + name + " theme from " + getBackupThemeDirectory());
 121  2692
     copy(blogTheme, getPathToLiveTheme());
 122  2692
   }
 123  
 
 124  
   /**
 125  
    * Restores the theme from the blog.dir to the webapp.
 126  
    */
 127  
   public void restoreToSpecifiedTheme(String themeName) {
 128  0
     File blogTheme = new File(getBackupThemeDirectory());
 129  0
     FileUtils.deleteFile(blogTheme);
 130  0
     FileUtils.deleteFile(getPathToLiveTheme());
 131  0
     restore(themeName);
 132  0
   }
 133  
 
 134  
   /**
 135  
    * Backs up the theme from the webapp to the blog.dir.
 136  
    */
 137  
   public void backup() {
 138  2696
     backup(name);
 139  2696
   }
 140  
 
 141  
   /**
 142  
    * Backs up the named theme from the webapp to the blog.dir.
 143  
    *
 144  
    * @param themeName the name of the theme to backup
 145  
    */
 146  
   private void backup(String themeName) {
 147  2696
     log.debug("Backing up " + themeName + " theme to " + getBackupThemeDirectory());
 148  2696
     File liveTheme = new File(pathToLiveThemes, themeName);
 149  2696
     File blogTheme = new File(getBackupThemeDirectory());
 150  2696
     File blogThemeBackup = getUniqueBackupBackupDirectory();
 151  
 
 152  2696
     if (blogTheme.exists()) {
 153  2692
       blogTheme.renameTo(blogThemeBackup);
 154  
     }
 155  2696
     copy(liveTheme, blogTheme);
 156  2696
     FileUtils.deleteFile(blogThemeBackup);
 157  2696
   }
 158  
 
 159  
   /**
 160  
    * Copies the named theme from the webapp to blog.dir/theme.
 161  
    *
 162  
    * @param themeName the name of the theme to backup
 163  
    */
 164  
   private void copy(String themeName) {
 165  2692
     log.info("Copying " + themeName + " theme to " + getBackupThemeDirectory());
 166  2692
     File liveTheme = new File(pathToLiveThemes, themeName);
 167  2692
     File blogTheme = new File(getBackupThemeDirectory());
 168  2692
     File blogThemeBackup = getUniqueBackupBackupDirectory();
 169  
 
 170  2692
     if (blogTheme.exists()) {
 171  4
       blogTheme.renameTo(blogThemeBackup);
 172  
     }
 173  2692
     copy(liveTheme, blogTheme);
 174  2692
     FileUtils.deleteFile(blogThemeBackup);
 175  2692
   }
 176  
 
 177  
   /**
 178  
    * Copies one file to another.
 179  
    *
 180  
    * @param source      the source
 181  
    * @param destination the destination
 182  
    */
 183  
   private void copy(File source, File destination) {
 184  8080
     if (!destination.exists()) {
 185  8076
       destination.mkdir();
 186  
     }
 187  
 
 188  8080
     File files[] = source.listFiles();
 189  8080
     if (files != null) {
 190  5376
       for (int i = 0; i < files.length; i++) {
 191  0
         if (files[i].isDirectory()) {
 192  0
           copy(files[i], new File(destination, files[i].getName()));
 193  
         } else {
 194  0
           FileInputStream is = null;
 195  0
           FileOutputStream os = null;
 196  
           try {
 197  0
             is = new FileInputStream(files[i]);
 198  0
             FileChannel srcChannel = is.getChannel();
 199  0
             long size = srcChannel.size();
 200  0
             os = new FileOutputStream(new File(destination, files[i].getName()));
 201  0
             FileChannel dstChannel = os.getChannel();
 202  0
             dstChannel.transferFrom(srcChannel, 0, size);
 203  0
           } catch (IOException ioe) {
 204  
             // We MUST throw an exception here, otherwise very bad things will happen.  For example, if some error
 205  
             // prevented us from copying a theme file from the blog directory to the live directory, if we don't
 206  
             // complain now, Pebble will happily start up, and that file will end up blank.  Then, when pebble shuts
 207  
             // down, backup will be called, and the blank file will be copied over the file in the blog directory, and
 208  
             // so that file will be lost.  It's simple to reproduce this, just shutdown pebble while its coming up,
 209  
             // Tomcat will interrupt the thread that is bringing it up and consequently all NIO the operations above will
 210  
             // throw exceptions.  Pebble never checks if the threads been interrupted, so if you ignore the exceptions,
 211  
             // pebble keeps on coming up, each time writing a blank file to the webapp directory.  Then Tomcat shuts
 212  
             // down the context listener, and that causes backup to be called, the thread is now not interrupted, and
 213  
             // so backup successfully writes all our blank files back to the blog directory, and all your themes are
 214  
             // lost.  I speak from experience.  Thankfully I had backups.
 215  0
             throw new RuntimeException("Error copying files", ioe);
 216  
           } finally {
 217  0
             IOUtils.closeQuietly(is);
 218  0
             IOUtils.closeQuietly(os);
 219  0
           }
 220  
         }
 221  
       }
 222  
     }
 223  8080
   }
 224  
 
 225  
   /**
 226  
    * If there is still a backup directory lying around, it means that something went wrong in a previous shutdown.  We
 227  
    * don't want to use it in case a user is starting/restarting pebble multiple times and hasn't realised they've
 228  
    * corrupted their theme yet.
 229  
    *
 230  
    * @return A unique directory name
 231  
    */
 232  
   private File getUniqueBackupBackupDirectory() {
 233  5388
     File dir = new File(getBackupThemeDirectory() + ".bak");
 234  5388
     if (dir.exists()) {
 235  0
       dir = new File(getBackupThemeDirectory() + "-" + System.currentTimeMillis() + ".bak");
 236  
     }
 237  5388
     return dir;
 238  
   }
 239  
 
 240  
   /**
 241  
    * Gets the name of this theme.
 242  
    *
 243  
    * @return the name
 244  
    */
 245  
   public String getName() {
 246  4
     return name;
 247  
   }
 248  
 
 249  
 }