001: /*
002: JSPWiki - a JSP-based WikiWiki clone.
003:
004: Copyright (C) 2001-2002 Janne Jalkanen (Janne.Jalkanen@iki.fi)
005:
006: This program is free software; you can redistribute it and/or modify
007: it under the terms of the GNU Lesser General Public License as published by
008: the Free Software Foundation; either version 2.1 of the License, or
009: (at your option) any later version.
010:
011: This program is distributed in the hope that it will be useful,
012: but WITHOUT ANY WARRANTY; without even the implied warranty of
013: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: GNU Lesser General Public License for more details.
015:
016: You should have received a copy of the GNU Lesser General Public License
017: along with this program; if not, write to the Free Software
018: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: */
020: package com.ecyrd.jspwiki.providers;
021:
022: import java.io.*;
023: import java.util.Properties;
024: import java.util.Collection;
025: import java.util.Date;
026: import java.util.TreeSet;
027: import java.util.ArrayList;
028: import java.util.List;
029: import org.apache.log4j.Logger;
030:
031: import com.ecyrd.jspwiki.*;
032:
033: /**
034: * Provides a simple directory based repository for Wiki pages.
035: * <P>
036: * All files have ".txt" appended to make life easier for those
037: * who insist on using Windows or other software which makes assumptions
038: * on the files contents based on its name.
039: * <p>
040: * This class functions as a superclass to all file based providers.
041: *
042: * @since 2.1.21.
043: *
044: * @author Janne Jalkanen
045: */
046: public abstract class AbstractFileProvider implements WikiPageProvider {
047: private static final Logger log = Logger
048: .getLogger(AbstractFileProvider.class);
049: private String m_pageDirectory = "/tmp/";
050:
051: protected String m_encoding;
052:
053: protected WikiEngine m_engine;
054:
055: /**
056: * Name of the property that defines where page directories are.
057: */
058: public static final String PROP_PAGEDIR = "jspwiki.fileSystemProvider.pageDir";
059:
060: /**
061: * All files should have this extension to be recognized as JSPWiki files.
062: * We default to .txt, because that is probably easiest for Windows users,
063: * and guarantees correct handling.
064: */
065: public static final String FILE_EXT = ".txt";
066:
067: public static final String DEFAULT_ENCODING = "ISO-8859-1";
068:
069: private boolean m_windowsHackNeeded = false;
070:
071: /**
072: * @throws FileNotFoundException If the specified page directory does not exist.
073: * @throws IOException In case the specified page directory is a file, not a directory.
074: */
075: public void initialize(WikiEngine engine, Properties properties)
076: throws NoRequiredPropertyException, IOException {
077: log.debug("Initing FileSystemProvider");
078: m_pageDirectory = WikiEngine.getRequiredProperty(properties,
079: PROP_PAGEDIR);
080:
081: File f = new File(m_pageDirectory);
082:
083: if (!f.exists()) {
084: f.mkdirs();
085: } else if (!f.isDirectory()) {
086: throw new IOException("Page directory is not a directory: "
087: + m_pageDirectory);
088: }
089:
090: m_engine = engine;
091:
092: m_encoding = properties.getProperty(WikiEngine.PROP_ENCODING,
093: DEFAULT_ENCODING);
094:
095: String os = System.getProperty("os.name").toLowerCase();
096:
097: if (os.startsWith("windows") || os.equals("nt")) {
098: m_windowsHackNeeded = true;
099: }
100:
101: log.info("Wikipages are read from '" + m_pageDirectory + "'");
102: }
103:
104: String getPageDirectory() {
105: return m_pageDirectory;
106: }
107:
108: private static final String[] WINDOWS_DEVICE_NAMES = { "con",
109: "prn", "nul", "aux", "lpt1", "lpt2", "lpt3", "lpt4",
110: "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", "com1", "com2",
111: "com3", "com4", "com5", "com6", "com7", "com8", "com9" };
112:
113: /**
114: * This makes sure that the queried page name
115: * is still readable by the file system.
116: */
117: protected String mangleName(String pagename) {
118: pagename = TextUtil.urlEncode(pagename, m_encoding);
119:
120: pagename = TextUtil.replaceString(pagename, "/", "%2F");
121:
122: if (m_windowsHackNeeded) {
123: String pn = pagename.toLowerCase();
124: for (int i = 0; i < WINDOWS_DEVICE_NAMES.length; i++) {
125: if (WINDOWS_DEVICE_NAMES[i].equals(pn)) {
126: pagename = "$$$" + pagename;
127: }
128: }
129: }
130:
131: return pagename;
132: }
133:
134: /**
135: * This makes the reverse of mangleName.
136: */
137: protected String unmangleName(String filename) {
138: // The exception should never happen.
139: try {
140: if (m_windowsHackNeeded && filename.startsWith("$$$")
141: && filename.length() > 3) {
142: filename = filename.substring(3);
143: }
144:
145: return TextUtil.urlDecode(filename, m_encoding);
146: } catch (UnsupportedEncodingException e) {
147: throw new InternalWikiException(
148: "Faulty encoding; should never happen");
149: }
150: }
151:
152: /**
153: * Finds a Wiki page from the page repository.
154: */
155: protected File findPage(String page) {
156: return new File(m_pageDirectory, mangleName(page) + FILE_EXT);
157: }
158:
159: public boolean pageExists(String page) {
160: File pagefile = findPage(page);
161:
162: return pagefile.exists();
163: }
164:
165: /**
166: * This implementation just returns the current version, as filesystem
167: * does not provide versioning information for now.
168: */
169: public String getPageText(String page, int version)
170: throws ProviderException {
171: return getPageText(page);
172: }
173:
174: /**
175: * Read the text directly from the correct file.
176: */
177: private String getPageText(String page) {
178: String result = null;
179: InputStream in = null;
180:
181: File pagedata = findPage(page);
182:
183: if (pagedata.exists()) {
184: if (pagedata.canRead()) {
185: try {
186: in = new FileInputStream(pagedata);
187: result = FileUtil.readContents(in, m_encoding);
188: } catch (IOException e) {
189: log.error("Failed to read", e);
190: } finally {
191: try {
192: if (in != null)
193: in.close();
194: } catch (Exception e) {
195: log.fatal("Closing failed", e);
196: }
197: }
198: } else {
199: log.warn("Failed to read page '" + page + "' from '"
200: + pagedata.getAbsolutePath()
201: + "', possibly a permissions problem");
202: }
203: } else {
204: // This is okay.
205: log.info("New page '" + page + "'");
206: }
207:
208: return result;
209: }
210:
211: public void putPageText(WikiPage page, String text)
212: throws ProviderException {
213: File file = findPage(page.getName());
214: PrintWriter out = null;
215:
216: try {
217: out = new PrintWriter(new OutputStreamWriter(
218: new FileOutputStream(file), m_encoding));
219:
220: out.print(text);
221: } catch (IOException e) {
222: log.error("Saving failed");
223: } finally {
224: if (out != null)
225: out.close();
226: }
227: }
228:
229: public Collection getAllPages() throws ProviderException {
230: log.debug("Getting all pages...");
231:
232: ArrayList set = new ArrayList();
233:
234: File wikipagedir = new File(m_pageDirectory);
235:
236: File[] wikipages = wikipagedir.listFiles(new WikiFileFilter());
237:
238: if (wikipages == null) {
239: log.error("Wikipages directory '" + m_pageDirectory
240: + "' does not exist! Please check " + PROP_PAGEDIR
241: + " in jspwiki.properties.");
242: throw new InternalWikiException(
243: "Page directory does not exist");
244: }
245:
246: for (int i = 0; i < wikipages.length; i++) {
247: String wikiname = wikipages[i].getName();
248: int cutpoint = wikiname.lastIndexOf(FILE_EXT);
249:
250: WikiPage page = getPageInfo(unmangleName(wikiname
251: .substring(0, cutpoint)),
252: WikiPageProvider.LATEST_VERSION);
253: if (page == null) {
254: // This should not really happen.
255: // FIXME: Should we throw an exception here?
256: log
257: .error("Page "
258: + wikiname
259: + " was found in directory listing, but could not be located individually.");
260: continue;
261: }
262:
263: set.add(page);
264: }
265:
266: return set;
267: }
268:
269: public Collection getAllChangedSince(Date date) {
270: return new ArrayList(); // FIXME
271: }
272:
273: public int getPageCount() {
274: File wikipagedir = new File(m_pageDirectory);
275:
276: File[] wikipages = wikipagedir.listFiles(new WikiFileFilter());
277:
278: return wikipages.length;
279: }
280:
281: /**
282: * Iterates through all WikiPages, matches them against the given query,
283: * and returns a Collection of SearchResult objects.
284: */
285: public Collection findPages(QueryItem[] query) {
286: File wikipagedir = new File(m_pageDirectory);
287: TreeSet res = new TreeSet(new SearchResultComparator());
288: SearchMatcher matcher = new SearchMatcher(m_engine, query);
289:
290: File[] wikipages = wikipagedir.listFiles(new WikiFileFilter());
291:
292: for (int i = 0; i < wikipages.length; i++) {
293: FileInputStream input = null;
294:
295: // log.debug("Searching page "+wikipages[i].getPath() );
296:
297: String filename = wikipages[i].getName();
298: int cutpoint = filename.lastIndexOf(FILE_EXT);
299: String wikiname = filename.substring(0, cutpoint);
300:
301: wikiname = unmangleName(wikiname);
302:
303: try {
304: input = new FileInputStream(wikipages[i]);
305: String pagetext = FileUtil.readContents(input,
306: m_encoding);
307: SearchResult comparison = matcher.matchPageContent(
308: wikiname, pagetext);
309: if (comparison != null) {
310: res.add(comparison);
311: }
312: } catch (IOException e) {
313: log.error("Failed to read " + filename, e);
314: } finally {
315: try {
316: if (input != null)
317: input.close();
318: } catch (IOException e) {
319: } // It's fine to fail silently.
320: }
321: }
322:
323: return res;
324: }
325:
326: /**
327: * Always returns the latest version, since FileSystemProvider
328: * does not support versioning.
329: */
330: public WikiPage getPageInfo(String page, int version)
331: throws ProviderException {
332: File file = findPage(page);
333:
334: if (!file.exists()) {
335: return null;
336: }
337:
338: WikiPage p = new WikiPage(m_engine, page);
339: p.setLastModified(new Date(file.lastModified()));
340:
341: return p;
342: }
343:
344: /**
345: * The FileSystemProvider provides only one version.
346: */
347: public List getVersionHistory(String page) throws ProviderException {
348: ArrayList list = new ArrayList();
349:
350: list.add(getPageInfo(page, WikiPageProvider.LATEST_VERSION));
351:
352: return list;
353: }
354:
355: public String getProviderInfo() {
356: return "";
357: }
358:
359: public void deleteVersion(String pageName, int version)
360: throws ProviderException {
361: if (version == WikiProvider.LATEST_VERSION) {
362: File f = findPage(pageName);
363:
364: f.delete();
365: }
366: }
367:
368: public void deletePage(String pageName) throws ProviderException {
369: File f = findPage(pageName);
370:
371: f.delete();
372: }
373:
374: public static class WikiFileFilter implements FilenameFilter {
375: public boolean accept(File dir, String name) {
376: return name.endsWith(FILE_EXT);
377: }
378: }
379: }
|