001    package crisp.wbwiki.server;
002    
003    import java.beans.XMLDecoder;
004    import java.beans.XMLEncoder;
005    import java.io.File;
006    import java.io.FileInputStream;
007    import java.io.FileOutputStream;
008    import java.io.IOException;
009    import java.io.InputStream;
010    import java.io.OutputStream;
011    
012    import org.apache.log4j.Logger;
013    
014    import crisp.wbwiki.client.Page;
015    
016    /**
017     * Implementation of WikiDb that stores to a text file.
018     * 
019     * @author Henrik Kniberg
020     */
021    public class WikiDbTextFile implements WikiDb {
022            private Logger log = Logger.getLogger(getClass());
023            private File path;
024            
025            /**
026             * @param path Path to the file used to store wiki data (required).
027             */
028            public WikiDbTextFile(File path) {
029                    if (path == null) {
030                            throw new IllegalArgumentException("No path provided");
031                    }
032                    this.path = path;
033            }
034            
035            /**
036             * Path to the file used to store wiki data. Never null.
037             */
038            public File getPath() {
039                    return path;
040            }
041            
042            public synchronized Page loadPage() {
043                    if (path.exists() && path.length() > 0) {
044                            log.debug("Loading from " + path.getAbsolutePath());
045                            InputStream stream = null;
046                            try {
047                                    stream = new FileInputStream(path);
048                                    XMLDecoder decoder = new XMLDecoder(stream);
049                                    Page page = (Page) decoder.readObject();
050                                    decoder.close();
051                                    return page;
052                                    
053                            } catch (IOException e) {
054                                    throw new RuntimeException("Problem while trying to read " + path.getAbsolutePath(), e);
055                            } finally {
056                                    try {
057                                            stream.close();
058                                    } catch (IOException err) {
059                                            log.error("Error while closing stream. Ignoring it and moving on.", err);
060                                    }                       
061                            }
062                    } else {
063                            log.debug("File doesn't exist or is empty: " + path.getAbsolutePath());
064    
065                            return new Page(1, "");
066                    }
067            }
068    
069            /**
070             * Saves the page and increments the version.
071             * @param pageContent
072             */
073            public synchronized int savePage(String pageContent) {
074                    if (pageContent == null) {
075                            pageContent = "";
076                    }
077                    int version = getCurrentVersion() + 1;
078                    Page newPage = new Page(version, pageContent);
079                    
080                    OutputStream stream = null;
081                    try {
082                            stream = new FileOutputStream(path, false);
083                            XMLEncoder encoder = new XMLEncoder(stream);
084                            encoder.writeObject(newPage);
085                            encoder.flush();
086                            encoder.close();
087                            return version;
088                    } catch (IOException e) {
089                            throw new RuntimeException("Problem while trying to write to " + path.getAbsolutePath(), e);
090                    } finally {
091                            try {
092                                    stream.close();
093                            } catch (IOException err) {
094                                    log.error("Error while closing stream. Ignoring it and moving on.", err);
095                            }                       
096                    }
097            }
098            
099            public synchronized int getCurrentVersion() {
100                    //@optimize this should probably be cached
101                    return loadPage().getVersion();         
102            }
103    
104    }