/** * ForumServer.java 1.00 1.28.97 Michael Shoffner * * Copyright (c) 1997 Prominence Dot Com, Inc. All Rights Reserved. * * Permission to use, copy, modify, and distribute this software * for non-commercial purposes and without fee is hereby granted * provided that this copyright notice appears in all copies. * * http://prominence.com/ shoffner@prominence.com */ import java.net.*; import java.util.*; import java.io.*; import ForumConnectionHandler; public class ForumServer extends Thread { static final String DEFAULT_FORUM_CONFIGFILE = "forum.cfg"; static final String CONFIGFILE_THREADS = "threads"; static final String CONFIGFILE_DATABASE = "database"; // port for server to listen for Forum clients static final int FORUM_PORT = 5000; Hashtable articles; String databaseFilename; long connections = 0; long memoryLimit, diskLimit; Vector acl; ThreadGroup handlers; ServerSocket listener; String argv[]; public ForumServer (String argv[]) { handlers = new ThreadGroup ("Forum Connection Handlers"); articles = new Hashtable(); this.argv = argv; } public void run() { try { setup (argv); listener = new ServerSocket (FORUM_PORT); while (true) { Socket c = listener.accept(); if (aclCheck (c)) { connections ++; ForumConnectionHandler h = new ForumConnectionHandler (connections, c, memoryLimit, articles, handlers); h.start(); } else { // could send client a "denied" message here InetAddress denied = c.getInetAddress(); System.out.println ("Denied connection from: " + denied + ".\n"); try { c.close(); } catch (IOException ex) { System.out.println ("Denied close failed: " + ex + ".\n"); } } } } catch (IOException ex) { // this is to ignore bug when closing down ServerSocket if (!(ex instanceof SocketException)) ex.printStackTrace(); } finally { try { if (listener != null) listener.close(); } catch (IOException ex) { System.out.println ("ServerSocket close failed.\n"); } handlers.stop(); try { if (databaseFilename != null && !databaseFilename.equals("")) { if (diskLimitCheck()) dumpArticlesToDatabase (databaseFilename); } else System.out.println ("Article database not saved to disk.\n"); } catch (IOException ex) { System.out.println ("Article database save failed: " + ex + ".\n"); } System.out.println ("The server has shut down."); } } void setup (String argv[]) throws IOException { // read from config file String configFileName = DEFAULT_FORUM_CONFIGFILE; if (argv.length > 0) configFileName = argv[0]; File configFile = new File (configFileName); if (! configFile.exists()) { throw new IOException ("Config file " + configFileName + " not found.\nSpecify a an alternate config file or provide a file named " + DEFAULT_FORUM_CONFIGFILE + ".\n"); } Properties config = getConfig (configFile); // load in previous article database, ignoring old threads databaseFilename = config.getProperty (CONFIGFILE_DATABASE); String t = config.getProperty (CONFIGFILE_THREADS); articles = loadArticles (databaseFilename, t); // load in memoryLimit, diskLimit here // load in inbound connection ACL here // print config to console System.out.println ("FORUM SERVER CONFIGURATION\n"); System.out.println ("Discussion threads:\n"); Enumeration en = articles.keys(); while (en.hasMoreElements()) System.out.println (en.nextElement()); System.out.println (""); System.out.println ("Database file: " + databaseFilename + "\n"); // System.out.println ("Maximum disk usage: "); // System.out.println ("IP restrictions: "); // System.out.println ("Maximum memory usage: "); System.out.println ("Client connection listing:\n"); } Properties getConfig (File configFile) throws IOException { Properties props = new Properties(); FileInputStream f = new FileInputStream (configFile); DataInputStream dIn = null; try { dIn = new DataInputStream (f); String line; int lineNo = 0; while ((line = dIn.readLine()) != null) { ++ lineNo; line = line.trim(); if ((line.length() > 0) && (!line.startsWith ("#"))) { int idx = line.indexOf ("="); if (idx >= 0) props.put (line.substring (0, idx).trim(), line.substring (idx + 1).trim()); else throw new IOException ("line " + lineNo + " not understood: " + line + "\n"); } } } catch (IOException ex) { throw new IOException ("Error reading " + configFile + ": " + ex.getMessage()); } finally { if (dIn != null) dIn.close(); } return props; } boolean aclCheck (Socket c) { // unimplemented return true; } boolean diskLimitCheck() { // unimplemented return true; } Hashtable loadArticles (String dbfn, String t) throws IOException { Hashtable returnArticles = new Hashtable(); if (t == null || t.equals("")) { throw new RuntimeException ("You must define at least one discussion thread in the configuration file.\n"); } else { StringTokenizer strtok = new StringTokenizer (t, ","); while (strtok.hasMoreTokens()) returnArticles.put (strtok.nextToken(), new Vector()); } if (dbfn != null && !dbfn.equals("")) { File db = new File (dbfn); System.out.println ("Threads loaded from filesystem:\n"); try { FileInputStream f = new FileInputStream (db); DataInputStream dIn = new DataInputStream (f); int numThreads = 0; numThreads = dIn.readInt(); for (int i = 1; i <= numThreads; i++) { String thread = dIn.readUTF(); Vector v = new Vector(); int numArts = 0; numArts = dIn.readInt(); for (int j = 1; j <= numArts; j++) { String art = dIn.readUTF(); v.addElement (art); } if (returnArticles.get (thread) != null) { returnArticles.put (thread, v); System.out.println ("Thread '" + thread + "' picked up from database file.\n"); } else System.out.println ("Thread '" + thread + "' ignored.\n"); } } catch (IOException ex) { System.out.println ("Exception '" + ex + "' encountered while attempting to load articles from database " + dbfn + ".\n"); } } return returnArticles; } void dumpArticlesToDatabase (String dbfn) throws IOException { if (articlesIsEmpty (articles)) { System.out.println ("No articles to save to database file.\n"); return; } /* ** database structure int: number of threads string: threads 1 name int: number of thread 1 articles thread 1's articles etc. */ int numThreads = articles.size(); Enumeration threads = articles.keys(); File db = new File (dbfn); DataOutputStream dOut = null; try { FileOutputStream f = new FileOutputStream (db); dOut = new DataOutputStream (f); // record the number of threads dOut.writeInt (numThreads); // put each thread in, followed by its articles while (threads.hasMoreElements()) { String curThread = (String) threads.nextElement(); dOut.writeUTF (curThread); Vector curThreadArts = (Vector) articles.get (curThread); dOut.writeInt (curThreadArts.size()); Enumeration allArts = curThreadArts.elements(); while (allArts.hasMoreElements()) { String art = (String) allArts.nextElement(); dOut.writeUTF (art); } } System.out.println ("Article database written to " + dbfn + ".\n"); } catch (IOException ex) { throw new IOException ("Exception " + ex + " encountered while attempting to write to file " + dbfn + ".\n"); } finally { try { if (dOut != null) dOut.close(); } catch (IOException ex) { throw new IOException ("Exception " + ex + " was encountered while attempting to close file " + dbfn + ".\n"); } } } boolean articlesIsEmpty (Hashtable a) { boolean result = true; if (a == null || a.size() == 0) return true; Enumeration en = a.keys(); while (en.hasMoreElements()) { Vector threadArts = (Vector) a.get (en.nextElement()); if (threadArts.size() > 0) { result = false; break; } } return result; } public static void main (String argv[]) throws IOException { ForumServer accept = new ForumServer (argv); accept.start(); // listen for keypress while (true) { Thread.yield(); if (System.in.available() != 0) { while (System.in.available() > 0) System.in.read(); accept.stop(); break; } } } }