001: /* CVS ID: $Id: FileStorage.java,v 1.1.1.1 2002/10/02 18:42:53 wastl Exp $ */
002: package net.wastl.webmail.storage;
003:
004: import java.io.*;
005: import java.text.*;
006: import java.util.*;
007: import java.util.zip.*;
008: import net.wastl.webmail.exceptions.*;
009: import net.wastl.webmail.server.*;
010: import net.wastl.webmail.config.*;
011: import net.wastl.webmail.misc.*;
012: import net.wastl.webmail.xml.*;
013: import net.wastl.webmail.logger.*;
014:
015: import javax.xml.transform.*;
016: import javax.xml.transform.stream.*;
017: import javax.xml.transform.dom.*;
018:
019: /**
020: * FileStorage.java
021: *
022: * Created: Jan 2000
023: *
024: * Copyright (C) 2000 Sebastian Schaffert
025: *
026: * This program is free software; you can redistribute it and/or
027: * modify it under the terms of the GNU General Public License
028: * as published by the Free Software Foundation; either version 2
029: * of the License, or (at your option) any later version.
030: *
031: * This program is distributed in the hope that it will be useful,
032: * but WITHOUT ANY WARRANTY; without even the implied warranty of
033: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
034: * GNU General Public License for more details.
035: *
036: * You should have received a copy of the GNU General Public License
037: * along with this program; if not, write to the Free Software
038: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
039: */
040:
041: /**
042: * This is the FileStorage class is common to all other storage classes in WebMail
043: * It provides means of getting and storing data in ZIPFiles and ResourceBundles,
044: * for example HTML-templates, binary files and MIME-types
045: *
046: * @see Storage
047: * @author Sebastian Schaffert
048: * @versin $Revision: 1.1.1.1 $
049: */
050: public abstract class FileStorage extends Storage implements
051: ConfigurationListener {
052:
053: protected Hashtable resources;
054:
055: protected Hashtable file_resources;
056:
057: protected Hashtable stylesheet_cache;
058: protected Hashtable binary_cache;
059:
060: /** Stores Locale/ExpireableCache pairs */
061: //protected Hashtable file_cache;;
062: protected Authenticator auth;
063:
064: protected static Hashtable mime_types;
065:
066: protected Logger logger;
067:
068: protected static DateFormat df = null;
069:
070: private boolean init_complete = false;
071:
072: protected int file_cache_size = 40;
073:
074: /**
075: * Initialize SimpleStorage.
076: * Fetch Configuration Information etc.
077: */
078: public FileStorage(WebMailServer parent) {
079: super (parent);
080:
081: initConfig();
082:
083: cs.addConfigurationListener("AUTH", this );
084: cs.configRegisterStringKey(this , "MIME TYPES", parent
085: .getProperty("webmail.lib.path")
086: + System.getProperty("file.separator") + "mime.types",
087: "File with mime type information.");
088:
089: cs
090: .configRegisterYesNoKey(
091: "SHOW ADVERTISEMENTS",
092: "Whether or not to include the WebMail advertisement "
093: + "messages in default user signatures and HTTP response headers");
094: cs.configRegisterStringKey("ADVERTISEMENT MESSAGE", "JWebMail "
095: + parent.getVersion() + " WWW to Mail Gateway",
096: "Advertisement to attach to user signatures");
097:
098: resources = new Hashtable();
099: file_resources = new Hashtable();
100:
101: initCache();
102:
103: initLog();
104:
105: // Now included in configuration:
106: // initVirtualDomains();
107:
108: initMIME();
109:
110: initAuth();
111:
112: initLanguages();
113:
114: init_complete = true;
115: }
116:
117: /**
118: * initialize XMLSystemData sysdata
119: */
120: protected abstract void initConfig();
121:
122: protected void initCache() {
123: // Initialize the file cache
124: cs.configRegisterIntegerKey(this , "CACHE SIZE FILE", "40",
125: "Size for the file system cache for every locale");
126: try {
127: file_cache_size = Integer.parseInt("CACHE SIZE FILE");
128: } catch (NumberFormatException e) {
129: }
130:
131: // Now the same for the stylesheet cache
132: if (stylesheet_cache == null) {
133: stylesheet_cache = new Hashtable(10);
134: }
135: Enumeration enum2 = stylesheet_cache.keys();
136: while (enum2.hasMoreElements()) {
137: ExpireableCache ec = (ExpireableCache) stylesheet_cache
138: .get(enum2.nextElement());
139: ec.setCapacity(file_cache_size);
140: }
141:
142: // And for binary files
143: if (binary_cache == null) {
144: binary_cache = new Hashtable(10);
145: }
146: Enumeration enum3 = binary_cache.keys();
147: while (enum3.hasMoreElements()) {
148: ExpireableCache ec = (ExpireableCache) binary_cache
149: .get(enum3.nextElement());
150: ec.setCapacity(file_cache_size);
151: }
152: }
153:
154: protected void initLog() {
155: if (false) {
156: try {
157: Class logger_class = Class.forName(parent
158: .getProperty("webmail.log.facility"));
159: Class[] argtypes = {
160: Class
161: .forName("net.wastl.webmail.server.WebMailServer"),
162: Class
163: .forName("net.wastl.webmail.server.Storage") };
164: Object[] args = { parent, this };
165: logger = (Logger) logger_class.getConstructor(argtypes)
166: .newInstance(args);
167: } catch (Exception ex) {
168: // Print a warning!
169: logger = new FileLogger(parent, this );
170: }
171: } else {
172: try {
173: logger = new ServletLogger(parent, this );
174: } catch (Exception ex) {
175: // Print a warning!
176: logger = new FileLogger(parent, this );
177: }
178:
179: }
180: }
181:
182: protected void initAuth() {
183: System.err.print(" * Authenticator ... ");
184: Authenticator a = parent.getAuthenticatorHandler()
185: .getAuthenticator(getConfig("AUTH"));
186: if (a != null) {
187: // IMAP level authentication
188: auth = a;
189: auth.init(this );
190: System.err.println("ok. Using " + auth.getClass().getName()
191: + " (v" + auth.getVersion()
192: + ") for authentication.");
193: } else {
194: System.err
195: .println("error. Could not initalize any authenticator. Users will not be able to log on.");
196: auth = null;
197: }
198: }
199:
200: protected void initMIME() {
201: System.err.print(" * MIME types ... ");
202: if (getConfig("mime types") != null) {
203: try {
204: File f = new File(getConfig("mime types"));
205: if (f.exists() && f.canRead()) {
206: mime_types = new Hashtable();
207: BufferedReader in = new BufferedReader(
208: new InputStreamReader(
209: new FileInputStream(f)));
210: String line = in.readLine();
211: while (line != null) {
212: if (!line.startsWith("#")) {
213: StringTokenizer tok = new StringTokenizer(
214: line);
215: if (tok.hasMoreTokens()) {
216: String type = tok.nextToken();
217: while (tok.hasMoreTokens()) {
218: String key = tok.nextToken();
219: mime_types.put(key, type);
220: //System.err.println(key+" -> "+type);
221: }
222: }
223: }
224: line = in.readLine();
225: }
226: in.close();
227: System.err.println(" loaded from "
228: + getConfig("mime types") + ".");
229: } else {
230: System.err.println(" could not find "
231: + getConfig("mime types")
232: + ". Will use standard MIME types.");
233: }
234: } catch (IOException ex) {
235: System.err.println(" could not find "
236: + getConfig("mime types")
237: + ". Will use standard MIME types.");
238: }
239: } else {
240: System.err
241: .println(" not configured. Will use standard MIME types.");
242: }
243: }
244:
245: protected void initLanguages() {
246: System.err.print(" * Available languages ... ");
247: File f=new File(parent.getProperty("webmail.template.path")+System.getProperty("file.separator"));
248: String[] flist=f.list(new FilenameFilter() {
249: public boolean accept(File myf, String s) {
250: if(myf.isDirectory() && s.equals(s.toLowerCase()) && (s.length()==2 || s.equals("default"))) {
251: return true;
252: } else {
253: return false;
254: }
255: }
256: });
257:
258: File cached=new File(parent.getProperty("webmail.data.path")+System.getProperty("file.separator")+"locales.cache");
259: Locale[] available1=null;
260:
261: /* Now we try to cache the Locale list since it takes really long to gather it! */
262: boolean exists=cached.exists();
263: if(exists) {
264: try {
265: ObjectInputStream in=new ObjectInputStream(new FileInputStream(cached));
266: available1=(Locale[])in.readObject();
267: in.close();
268: System.err.print(" using disk cache ... ");
269: } catch(Exception ex) {
270: exists=false;
271: }
272: }
273: if(!exists) {
274: // We should cache this on disk since it is so slow!
275: available1=Collator.getAvailableLocales();
276: try {
277: ObjectOutputStream os=new ObjectOutputStream(new FileOutputStream(cached));
278: os.writeObject(available1);
279: os.close();
280: } catch(Exception ex) {
281: ex.printStackTrace();
282: }
283: }
284:
285: // Do this manually, as it is not JDK 1.1 compatible ...
286: //Vector available=new Vector(Arrays.asList(available1));
287: Vector available=new Vector(available1.length);
288: for(int i=0; i<available1.length; i++) {
289: available.addElement(available1[i]);
290: }
291: String s="";
292: int count=0;
293: for(int i=0;i<flist.length;i++) {
294: String cur_lang=flist[i];
295: Locale loc=new Locale(cur_lang,"","");
296: Enumeration enum=available.elements();
297: boolean added=false;
298: while(enum.hasMoreElements()) {
299: Locale l=(Locale)enum.nextElement();
300: if(l.getLanguage().equals(loc.getLanguage())) {
301: s+=l.toString()+" ";
302: count++;
303: added=true;
304: }
305: }
306: if(!added) {
307: s+=loc.toString()+" ";
308: count++;
309: }
310: }
311: System.err.println(count+" languages initialized.");
312: cs.configRegisterStringKey(this ,"LANGUAGES",s,"Languages available in WebMail");
313: setConfig("LANGUAGES",s);
314:
315: /*
316: Setup list of themes for each language
317: */
318: for(int j=0;j<flist.length;j++) {
319: File themes=new File(parent.getProperty("webmail.template.path")+System.getProperty("file.separator")
320: +flist[j]+System.getProperty("file.separator"));
321: String[] themelist=themes.list(new FilenameFilter() {
322: public boolean accept(File myf, String s3) {
323: if(myf.isDirectory() && !s3.equals("CVS")) {
324: return true;
325: } else {
326: return false;
327: }
328: }
329: });
330: String s2="";
331: for(int k=0;k<themelist.length;k++) {
332: s2+=themelist[k]+" ";
333: }
334: cs.configRegisterStringKey(this ,"THEMES_"+flist[j].toUpperCase(),s2,"Themes for language "+flist[j]);
335: setConfig("THEMES_"+flist[j].toUpperCase(),s2);
336: }
337: }
338:
339: /**
340: * Get the String for key and the specified locale.
341: * @param key Identifier for the String
342: * @param locale locale of the String to fetch
343: */
344: public String getStringResource(String key, Locale locale) {
345: if (resources.get(locale.getLanguage()) != null) {
346: String s = ((ResourceBundle) resources.get(locale
347: .getLanguage())).getString(key);
348: return ((ResourceBundle) resources
349: .get(locale.getLanguage())).getString(key);
350: } else {
351: try {
352: // Modified by exce, start.
353: // ResourceBundle rc=XMLResourceBundle.getBundle("resources",locale,null);
354: System.err.println("Loading locale");
355: ResourceBundle rc = ResourceBundle.getBundle(
356: "org.bulbul.webmail.xmlresource.Resources",
357: locale);
358: // Modified by exce, end.
359: resources.put(locale.getLanguage(), rc);
360: return rc.getString(key);
361: } catch (Exception e) {
362: e.printStackTrace();
363: return "";
364: }
365: }
366: }
367:
368: /**
369: * Return the requested Stylesheet, precompiled and fitting to the locale and theme
370: */
371: public Templates getStylesheet(String name, Locale locale,
372: String theme) throws WebMailException {
373: String key = locale.getLanguage() + "/" + theme;
374:
375: AttributedExpireableCache cache = (AttributedExpireableCache) stylesheet_cache
376: .get(key);
377:
378: if (cache == null) {
379: cache = new AttributedExpireableCache(file_cache_size);
380: stylesheet_cache.put(key, cache);
381: }
382:
383: Templates stylesheet = null;
384:
385: String basepath = getBasePath(locale, theme);
386:
387: File f = new File(basepath + name);
388: if (!f.exists()) {
389: throw new StylesheetNotFoundException(
390: "The requested stylesheet " + name
391: + " could not be found (path tried: "
392: + basepath + ".");
393: }
394:
395: if (cache.get(name) != null
396: && ((Long) cache.getAttributes(name)).longValue() >= f
397: .lastModified()) {
398: // Keep statistics :-)
399: cache.hit();
400: return (Templates) cache.get(name);
401: } else {
402: try {
403: StreamSource msg_xsl = new StreamSource("file://"
404: + basepath + name);
405: TransformerFactory factory = TransformerFactory
406: .newInstance();
407: stylesheet = factory.newTemplates(msg_xsl);
408: cache.put(name, stylesheet, new Long(f.lastModified()));
409: cache.miss();
410: } catch (Exception ex) {
411: //System.err.println("Error while compiling stylesheet "+name+", language="+locale.getLanguage()+", theme="+theme+".");
412: throw new WebMailException(
413: "Error while compiling stylesheet " + name
414: + ", language=" + locale.getLanguage()
415: + ", theme=" + theme + ":\n"
416: + ex.toString());
417: }
418: return stylesheet;
419: }
420: }
421:
422: /**
423: * Get a binary file for the specified locale.
424: * @param key Identifier for the String
425: * @param locale locale of the String to fetch
426: */
427: public synchronized byte[] getBinaryFile(String name,
428: Locale locale, String theme) throws BinaryNotFoundException {
429: String key = locale.getLanguage() + "/" + theme;
430:
431: AttributedExpireableCache cache = (AttributedExpireableCache) binary_cache
432: .get(key);
433:
434: if (cache == null) {
435: cache = new AttributedExpireableCache(file_cache_size);
436: binary_cache.put(key, cache);
437: }
438:
439: ByteStore bs = null;
440:
441: String basepath = getBasePath(locale, theme);
442: File f = new File(basepath + name);
443: if (!f.exists()) {
444: throw new BinaryNotFoundException("The file " + name
445: + " could not be found!");
446: }
447:
448: if (cache.get(name) != null
449: && ((Long) cache.getAttributes(name)).longValue() >= f
450: .lastModified()) {
451: // Keep statistics :-)
452: cache.hit();
453: return ((ByteStore) cache.get(name)).getBytes();
454: } else {
455: try {
456: bs = ByteStore.getBinaryFromIS(new FileInputStream(f),
457: (int) f.length());
458: } catch (IOException ex) {
459: ex.printStackTrace();
460: }
461: cache.put(name, bs, new Long(f.lastModified()));
462:
463: if (bs != null) {
464: return bs.getBytes();
465: } else {
466: return new byte[1];
467: }
468: }
469: }
470:
471: public Authenticator getAuthenticator() {
472: return auth;
473: }
474:
475: /**
476: * Send a message to the logging facility.
477: * @param level severity level of the message
478: * @param message the message
479: */
480: public synchronized void log(int level, String message) {
481: if (logger != null) {
482: logger.log(level, message);
483: } else {
484: System.err.println("LOG(" + level + "): " + message);
485: }
486: }
487:
488: /**
489: * Send a message to the logging facility.
490: * @param level severity level of the message
491: * @param message the message
492: */
493: public synchronized void log(int level, Exception ex) {
494: if (logger != null) {
495: logger.log(level, ex);
496: } else {
497: System.err.println("LOG(" + level + "): ");
498: ex.printStackTrace();
499: }
500: }
501:
502: protected String formatDate(long date) {
503: if (df == null) {
504: TimeZone tz = TimeZone.getDefault();
505: df = DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
506: DateFormat.DEFAULT, Locale.getDefault());
507: df.setTimeZone(tz);
508: }
509: String now = df.format(new Date(date));
510: return now;
511: }
512:
513: public void shutdown() {
514: logger.shutdown();
515: }
516:
517: public String getMimeType(String name) {
518: if(mime_types == null) {
519: return super .getMimeType(name);
520: } else {
521: if(name != null) {
522: String type="application/unknown";
523: Enumeration enum=mime_types.keys();
524: while(enum.hasMoreElements()) {
525: String s=(String)enum.nextElement();
526: if(name.toLowerCase().endsWith(s)) {
527: type= (String)mime_types.get(s);
528: }
529: }
530: return type;
531: } else {
532: return "UNKNOWN";
533: }
534: }
535: }
536:
537: public void notifyConfigurationChange(String key) {
538: log(Storage.LOG_DEBUG,
539: "FileStorage: Configuration change notify for key "
540: + key + ".");
541: System.err.println("- Configuration changed: ");
542: if (key.toUpperCase().startsWith("AUTH")) {
543: initAuth();
544: } else if (key.toUpperCase().startsWith("MIME")) {
545: initMIME();
546: }
547: }
548:
549: public String toString() {
550: String s="";
551: Enumeration enum=stylesheet_cache.keys();
552: while(enum.hasMoreElements()) {
553: String name=(String)enum.nextElement();
554: ExpireableCache cache=(ExpireableCache)stylesheet_cache.get(name);
555: s+=" - stylesheet cache for "+name+": Capacity "+cache.getCapacity()+", Usage "+cache.getUsage();
556: s+=", "+cache.getHits()+" hits, "+cache.getMisses()+" misses\n";
557: }
558: enum=binary_cache.keys();
559: while(enum.hasMoreElements()) {
560: String name=(String)enum.nextElement();
561: ExpireableCache cache=(ExpireableCache)binary_cache.get(name);
562: s+=" - binary cache for "+name+": Capacity "+cache.getCapacity()+", Usage "+cache.getUsage();
563: s+=", "+cache.getHits()+" hits, "+cache.getMisses()+" misses\n";
564: }
565: return s;
566: }
567:
568: }
|