bookkeepr.BookKeepr.java Source code

Java tutorial

Introduction

Here is the source code for bookkeepr.BookKeepr.java

Source

/*
 * Copyright (C) 2005-2007 Michael Keith, University Of Manchester
 * 
 * email: mkeith@pulsarastronomy.net
 * www  : www.pulsarastronomy.net
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package bookkeepr;

import bookkeepr.jettyhandlers.ArchivedStorageHandler;
import bookkeepr.jettyhandlers.BackgroundTaskHandler;
import bookkeepr.jettyhandlers.CandidateHandler;
import bookkeepr.jettyhandlers.FetchHandler;
import bookkeepr.jettyhandlers.ObservationHandler;
import bookkeepr.jettyhandlers.SystemHandler;
import bookkeepr.jettyhandlers.WebHandler;
import bookkeepr.managers.ArchiveManager;
import bookkeepr.managers.CandidateManager;
import bookkeepr.managers.ObservationManager;
import bookkeepr.managers.SessionManager;
import bookkeepr.managers.SyncManager;
import bookkeepr.xml.XMLReader;
import bookkeepr.xml.XMLWriter;
import bookkeepr.xmlable.BookkeeprConfig;
import bookkeepr.xmlable.BookkeeprHost;
import bookkeepr.xmlable.DatabaseManager;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.BindException;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.http.HttpHost;
import org.apache.http.client.HttpClient;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpConnectionParams;
import org.mortbay.jetty.Handler;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.handler.HandlerCollection;
import org.mortbay.thread.QueuedThreadPool;
import org.xml.sax.SAXException;

/**
 *
 * The master bookkeepr class.
 * 
 * Inistantiate it, set any parameters, call init() to configure and 
 * then start() to kick things off.
 * 
 * @author Michael Keith
 */
public class BookKeepr {

    private static final int version = 3;
    /*
     * The managers, that look after the data
     */
    DatabaseManager masterDatabaseManager; // this is the core database engine

    SessionManager sessionManager; // monitors the latest sessions from all servers

    SyncManager syncManager; // fetches data from other servers to keep in sync

    ObservationManager obsManager;
    ArchiveManager archiveManager;
    CandidateManager candManager;
    /*
     * The Jetty handlers that deal with incoming requests 
     */
    BackgroundTaskHandler bgHandler; // provides status on background tasks

    SystemHandler sysHandler; // provides interfaces to core system properties

    FetchHandler fetchHandler; // provides data on the system, and elements in the db

    ObservationHandler obsHandler; // provides advanced interfaces to the observation db.

    ArchivedStorageHandler archiveHandler; // provides interfaces for data archives (tapes).

    CandidateHandler candHandler; // provides advanced interfaces to the candidate db.

    WebHandler webHandler;
    BookKeeprStatusMonitor statusMon; // monitors the health of the server.

    /*
     * Background task runner...
     * This is a thread that pulls jobs of a queue to do tasks in serial even
     * if they are submitted in paralell. Helps to prevent database overloading
     * Tasks that are run with the bgrunner also won't interfere with each other.
     */
    BackgroundTaskRunner bgrunner = new BackgroundTaskRunner();
    Server server; // the Jetty Server

    BookkeeprConfig config; // our configuration file

    File configFile; // the configuration file File object.

    private Queue<HttpClient> httpClients = new ArrayBlockingQueue<HttpClient>(20);

    /**
     * This loads the configuration file and sets the intial settings. 
     */
    public BookKeepr(File configFile) {

        try {
            this.configFile = configFile;
            if (!configFile.exists()) {
                config = new BookkeeprConfig();
                config.setOriginId(0);
                saveConfig();
            }
            config = (BookkeeprConfig) XMLReader.read(new FileInputStream(configFile));
            if (config.getOriginId() < 0 || config.getOriginId() > 255) {
                config.setOriginId(0);

            }
            if (config.getOriginId() == 0) {
                Logger.getLogger(BookKeepr.class.getName()).log(Level.INFO,
                        "Client mode active, creation or modification disabled");
            }

            statusMon = new BookKeeprStatusMonitor();
            Logger logger = Logger.getLogger("bookkeepr");
            logger.setLevel(Level.ALL);
            logger.setUseParentHandlers(false);
            logger.addHandler(statusMon);
        } catch (SAXException ex) {
            Logger.getLogger(BookKeepr.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(BookKeepr.class.getName()).log(Level.SEVERE, null, ex);
        }
        for (int i = 0; i < 20; i++) {
            HttpClient httpclient = new DefaultHttpClient();
            HttpClientParams.setRedirecting(httpclient.getParams(), false);
            HttpConnectionParams.setConnectionTimeout(httpclient.getParams(), 10000);
            if (config.getProxyUrl() != null) {
                final HttpHost proxy = new HttpHost(config.getProxyUrl(), config.getProxyPort(), "http");
                httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
            }
            httpClients.add(httpclient);
        }

    }

    /**
     * Initialises the various managers and such.
     * Loads the database from the disk if it exists.
     * Configures, but does not start, the jetty server.
     * Once this is run, you should call start() to start the server.
     */
    public void init() {
        try {
            test();

            UpgradeChecker.pre_load(config.getRootPath(), this);

            saveConfig();

            File dbFile = new File(config.getRootPath() + File.separator + "_index.xml");
            if (dbFile.exists()) {
                Logger.getLogger(BookKeepr.class.getName()).log(Level.INFO, "Loading existing database");
                masterDatabaseManager = (DatabaseManager) XMLReader.read(new FileInputStream(dbFile));
                masterDatabaseManager.setOriginId(config.getOriginId());
                masterDatabaseManager.setRootPath(config.getRootPath());
            } else {
                Logger.getLogger(BookKeepr.class.getName()).log(Level.INFO, "Creating new database");
                masterDatabaseManager = new DatabaseManager();
                masterDatabaseManager.setOriginId(config.getOriginId());
                masterDatabaseManager.setRootPath(config.getRootPath());
                try {
                    masterDatabaseManager.save();
                } catch (BookKeeprException ex) {
                    Logger.getLogger(BookKeepr.class.getName()).log(Level.WARNING, null, ex);
                }

            }
            masterDatabaseManager.setBookkeepr(this);
            masterDatabaseManager.setExternalUrl(config.getExternalUrl());

            sessionManager = new SessionManager(masterDatabaseManager);
            syncManager = new SyncManager(masterDatabaseManager, config, sessionManager, bgrunner);
            obsManager = new ObservationManager(masterDatabaseManager);
            archiveManager = new ArchiveManager(masterDatabaseManager);
            candManager = new CandidateManager("./cands/", masterDatabaseManager, bgrunner);

            sysHandler = new SystemHandler(this);
            obsHandler = new ObservationHandler(this, masterDatabaseManager, obsManager);
            candHandler = new CandidateHandler(candManager, masterDatabaseManager, this);
            archiveHandler = new ArchivedStorageHandler(this, archiveManager);
            webHandler = new WebHandler(this, "./web/");
            masterDatabaseManager.restore();

        } catch (SAXException ex) {
            Logger.getLogger(BookKeepr.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(BookKeepr.class.getName()).log(Level.SEVERE, null, ex);
        }

        UpgradeChecker.post_load(config.getRootPath(), this);

        bgHandler = new BackgroundTaskHandler(bgrunner);
        fetchHandler = new FetchHandler(this);

        HandlerCollection handlers = new HandlerCollection();
        handlers.setHandlers(new Handler[] { sysHandler, fetchHandler, bgHandler, obsHandler, archiveHandler,
                candHandler, webHandler });

        server = new Server(config.getPort());

        final QueuedThreadPool threadPool = new QueuedThreadPool();
        threadPool.setMaxThreads(100);
        threadPool.setMinThreads(10);
        server.setThreadPool(threadPool);
        server.setHandler(handlers);

    }

    public void saveConfig() {
        try {
            XMLWriter.write(new FileOutputStream(configFile), config); // update the config with new defaults

        } catch (FileNotFoundException ex) {
            Logger.getLogger(BookKeepr.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public BookkeeprHost getHost() {
        BookkeeprHost host = new BookkeeprHost();
        host.setUrl(this.config.getExternalUrl());
        host.setOriginId(this.config.getOriginId());
        if (this.masterDatabaseManager != null) {
            host.setMaxOriginId(this.masterDatabaseManager.getMaxOriginId());
        }
        host.setStatus(statusMon.getStatus().toString());
        host.setErrors(statusMon.getNErr());
        host.setWarnings(statusMon.getNWarn());
        host.setVersion(version);

        return host;
    }

    public DatabaseManager getMasterDatabaseManager() {
        return masterDatabaseManager;
    }

    public BookKeeprStatusMonitor getStatusMon() {
        return statusMon;
    }

    /**
     * Call init() first.
     * 
     * Starts the bookkeepr system.
     * 
     * Starts the jetty server, the background task runner and the 
     * database synchronisation thread.
     * 
     */
    public void start() {
        try {

            bgrunner.start();
            syncManager.run();
            server.start();
            //test();
        } catch (BindException ex) {
            System.err.println("\n\n\nSomething (probably another Bookkeepr) is already using the bookkeepr port.");
            System.exit(1);
        } catch (Exception ex) {
            Logger.getLogger(BookKeepr.class.getName()).log(Level.SEVERE, null, ex);
        }

        Runtime.getRuntime().addShutdownHook(new Thread() {

            @Override
            public void run() {
                super.run();
                System.err.println("Shutting down the BookKeepr");
                BookKeepr.this.stop();
            }
        });
    }

    public void stop() {
        saveConfig();

        try {
            server.stop();
            syncManager.terminate();
            bgrunner.terminate();
        } catch (Exception ex) {
            Logger.getLogger(BookKeepr.class.getName()).log(Level.SEVERE, null, ex);
        }
        this.bgrunner.terminate();
    }

    public BookkeeprConfig getConfig() {
        return config;
    }

    public void setConfig(BookkeeprConfig config) {
        this.config = config;
    }

    public static void main(String[] args) {

        String file = "bookkeepr.cfg.xml";
        if (args.length > 0) {
            file = args[0];
        }
        BookKeepr bk = new BookKeepr(new File(file));

        for (int i = 1; i < args.length; i++) {
            if (args[i].equals("--addhost")) {
                BookkeeprHost host = new BookkeeprHost();
                host.setOriginId(-1);
                host.setUrl(args[++i]);
                bk.getConfig().addBookkeeprHost(host);
            }
        }

        bk.init();
        bk.start();

    }

    public BackgroundTaskRunner getBackgroundTaskRunner() {
        return bgrunner;
    }

    ArchiveManager getArchiveManager() {
        return archiveManager;
    }

    CandidateManager getCandManager() {
        return candManager;
    }

    ObservationManager getObsManager() {
        return obsManager;
    }

    SessionManager getSessionManager() {
        return sessionManager;
    }

    SyncManager getSyncManager() {
        return syncManager;
    }

    public HttpClient checkoutHttpClient() {

        HttpClient ret = null;
        while (ret == null) {
            synchronized (httpClients) {
                ret = httpClients.poll();
            }
            if (ret == null) {
                Logger.getLogger(BookKeepr.class.getName()).log(Level.WARNING,
                        "Run out of http handlers in BookKeepr!");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException ex) {
                }
            }
        }
        return ret;
    }

    public void returnHttpClient(HttpClient client) {
        this.httpClients.offer(client);
    }

    private void test() {
    }
}