package net.sourceforge.pain;
import net.sourceforge.pain.data.*;
import net.sourceforge.pain.data.role.*;
import net.sourceforge.pain.db.*;
import net.sourceforge.pain.logic.*;
import net.sourceforge.pain.network.console.*;
import net.sourceforge.pain.network.guitool.*;
import net.sourceforge.pain.plugin.*;
import net.sourceforge.pain.util.*;
import java.io.*;
import java.util.*;
/**
* Class that reads codebase startup settings, starts codebase and mudlib
* Codebase properties sample file:
<pre>
# NETWORK PARAMS
# Path to database file
pain.database_file=pain.db
# port for administrators service
pain.admin_server_port=7775
# Mud port
pain.mud_server_port=7777
# Port for GUITool service
pain.guitool_server_port=5555
# PLUGINS LAYER
# All plugins should be placed under the same package.
# This allows PAiN codebase to separate classloaders
# for static,logic and plugins code
# (every plugin is loaded in separate classloader)
pain.plugins_package_root=net.sourceforge.pain.tinylib.plugin
# Path to dir with compiled plugins classes
pain.plugins_classes_dir=../tinylib/classes
# LOGIC LAYER
# Set of comma-separated package names.
# All classes under these packages are treated as 'logic'
# This allows PAiN codebase to separate classloaders
# for static,logic and plugins code
# Logic classes are loaded by single 'logic' classloader
pain.logic_packages=net.sourceforge.pain.tinylib.logic
# Path to dir with compiled logic classes
pain.logic_classes_dir=../tinylib/classes
# All Affect impls should be located under the same package
# This allows to save in DB only short affects class name suffix
# Affects package should be child of one of the logic packages
pain.affects_logic_package=net.sourceforge.pain.tinylib.logic.affect
# All Trigger impls should be located under the same package
# This allows to save in DB only short trigger class name suffix
# Trigger package should be child of one of the logic packages
pain.triggers_logic_package=net.sourceforge.pain.tinylib.logic.trigger
# Objects instances are constructed from Prototypes
# by static factory methods (see ObjectFactory javadoc).
# Object factory name for specified <ROLE >are constructed
# from 'object_factory_package'+<ROLE>+"Factory"
# Object factories packahe should be child of one of the logic packages
pain.object_factory_package=net.sourceforge.pain.tinylib.logic.factory
# Codebase emits events (console events, system events) to mudlib using class
# name suffix. Corresponding mudlib-specific event impl class name
# constructed by concatinating 'logic_events_package' with such suffix
pain.logic_events_package=net.sourceforge.pain.tinylib.logic.event
#MUDLIB
#(optional)
# Mudlib could override(extend) user console class
# (add more field/fuctionality)
# default value: net.sourceforge.pain.network.console.BasicConsole
pain.user_console_factory=net.sourceforge.pain.tinylib.TinylibConsoleFactory
# Full class name of mudlib initializer impl
# After codebase environment is ready Mudlib initializer is called
# (see net.sourceforge.pain.MudlibInitializer javadoc)
pain.mudlib_initializer=net.sourceforge.pain.tinylib.TinyLibInitializer
tinylib.etc_dir=./etc
</pre>
*/
public class CodebaseLauncher {
private static PropertiesReader props;
private static CodebaseSettings settings;
private static MudlibInitializer initer;
/**
* Usage: CodebaseLauncher <codebase_properties_file>
*/
public static void main(String[] args) throws Exception {
if (args.length == 0) {
System.err.println("Usage: CodebaseLauncher <codebase_properties_file>");
}
props = new PropertiesReader(args[0]);
settings = new CodebaseSettings(props);
init();
}
private static void init() throws Exception {
try {
initer = (MudlibInitializer) settings.mudlibInitializer.newInstance();
initer.readSetting(props);//reading settings before database start
initDb();
runCoreTasksAdminConsoleService();
initMudlibAndUserServer();
runGUIToolService();
runUsersConsoleService();
//free mem
props = null;
settings = null;
runPulse();
} catch (Exception e) {
try {
Codebase.getPulse().stopPulse();
} catch (Exception e1) {
e.printStackTrace();
}
try {
final UserConsoleManager userConsoleManager = Codebase.getUserConsoleManager();
if (userConsoleManager != null && userConsoleManager.isStarted()) {
userConsoleManager.stop();
}
} catch (Exception e1) {
e1.printStackTrace();
}
try {
final AdminConsoleManager adminConsoleManager = Codebase.getAdminConsoleManager();
if (adminConsoleManager != null && adminConsoleManager.isStarted()) {
adminConsoleManager.stop();
}
} catch (Exception e1) {
e1.printStackTrace();
}
PainDB db = Codebase.getDB();
if (db != null) {
try {
db.close();
} catch (RuntimeException e2) {
e2.printStackTrace();
}
}
e.printStackTrace();
}
}
private static void initDb() throws Exception {
Log.info("initializing database :" + settings.dbFile.getAbsolutePath());
assert Codebase.getDB() == null;
PainDB db = new PainDB(settings.dbFile.getAbsolutePath());
if (db.isDatabaseEmpty()) {
prepareDefaultCodebaseDB(db);
}
Codebase.setDB(db);
Log.info("database INITED");
}
private static void runCoreTasksAdminConsoleService() throws Exception {
Log.info("initializing plugins and logic loaders..");
PluginManager plm = new PluginManager();
plm.init(settings.pluginsPackagePrefix, settings.pluginsClassesDir);
Codebase.setPluginManager(plm);
LogicLoader llm = new LogicLoader(settings.logicClassesDir);
llm.setEventsPackagePrefix(settings.logicEventsPackage + ".");
for (int i = 0; i < settings.logicPackages.length; i++) {
llm.addLogicPackage(settings.logicPackages[i]);
}
llm.addLogicPackage(settings.affectsLogicPackage);
Codebase.setLogicLoader(llm);
Log.info("initializing plugins and logic loaders DONE");
Log.info("Creating affect processor");
Codebase.getPulse().addListener(new AffectProcessor(Codebase.getCodebaseData().getAffectsQueue()));
Log.info("Creating affect processor OK");
Log.info("starting admin console service (port:" + settings.adminServerPort + ")..");
AdminConsoleManager acm = new AdminConsoleManager(settings.adminServerPort);
Codebase.setAdminConsoleManager(acm);
acm.start();
Log.info("admin console service STARTED");
}
private static void initMudlibAndUserServer() throws Exception {
Codebase.OBJECT_FACTORY_PACKAGE_PREFIX = settings.objectFactoryPackage + ".";
Codebase.AFFECTS_LOGIC_PACKAGE_PREFIX = settings.affectsLogicPackage + ".";
Codebase.TRIGGERS_LOGIC_PACKAGE_PREFIX = settings.triggersLogicPackage + ".";
Codebase.setUserConsoleManager(new UserConsoleManager(settings.userServerPort, (ConsoleFactory) settings.userConsoleFactoryClass.newInstance()));
Codebase.getPulse().addListener(new Flusher());
Log.info("initializing mudlib:" + settings.mudlibInitializer.getName());
initer.init();
Log.info("initializing mudlib: DONE");
}
private static void runGUIToolService() throws Exception {
Log.info("Starting GUITool service (port:" + settings.guiToolServicePort + ")...");
Codebase.setGuiToolSessionManager(new GuiToolSessionManager(settings.guiToolServicePort));
Log.info("GUITool service STARTED");
}
private static void runUsersConsoleService() throws Exception {
Log.info("Starting user console service (port:" + settings.userServerPort + ")...");
Codebase.getUserConsoleManager().start();
Log.info("User console service STARTED");
}
private static void runPulse() {
Log.info("starting codebase pulse...");
new Thread(Codebase.getPulse()).start();
Log.info("Pulse STARTED");
}
private static class CodebaseSettings {
final File dbFile;
final int adminServerPort;
final int userServerPort;
final int guiToolServicePort;
final Class mudlibInitializer;
final String pluginsPackagePrefix;
final String pluginsClassesDir;
final String[] logicPackages;
final String logicClassesDir;
final String logicEventsPackage;
final public String affectsLogicPackage;
final String objectFactoryPackage;
final Class userConsoleFactoryClass;
final String triggersLogicPackage;
CodebaseSettings(PropertiesReader pr) throws Exception {
dbFile = new File(pr.get("pain.database_file"));
adminServerPort = pr.getInt("pain.admin_server_port", 0);
userServerPort = pr.getInt("pain.mud_server_port", 0);
guiToolServicePort = pr.getInt("pain.guitool_server_port", 0);
if (adminServerPort == userServerPort || userServerPort == guiToolServicePort || guiToolServicePort == adminServerPort) {
throw new RuntimeException("Illegal ports");
}
mudlibInitializer = Class.forName(pr.get("pain.mudlib_initializer"));
if (!MudlibInitializer.class.isAssignableFrom(mudlibInitializer)) {
throw new RuntimeException("class MudlibInitializer is not assignable from :" + mudlibInitializer.getName());
}
mudlibInitializer.getConstructor(null);//check default constr;
final String tmp = props.get("pain.plugins_package_root");
pluginsPackagePrefix = tmp.endsWith(".") ? tmp : tmp + ".";
pluginsClassesDir = props.get("pain.plugins_classes_dir");
affectsLogicPackage = props.get("pain.affects_logic_package");
triggersLogicPackage = props.get("pain.triggers_logic_package");
objectFactoryPackage = props.get("pain.object_factory_package");
logicEventsPackage = props.get("pain.logic_events_package");
String logicPackagesStr = props.get("pain.logic_packages");
StringTokenizer st = new StringTokenizer(logicPackagesStr, ",");
logicPackages = new String[st.countTokens()];
boolean affectsOK = false;
boolean triggersOK = false;
boolean factoryOK = false;
boolean eventsOK = false;
for (int i = 0; st.hasMoreTokens(); i++) {
final String lp = st.nextToken();
if (lp.startsWith(pluginsPackagePrefix)) {
throw new RuntimeException("Logic vs Plugins package collission:" + lp);
}
if (affectsLogicPackage.startsWith(lp)) {
affectsOK = true;
}
if (triggersLogicPackage.startsWith(lp)) {
triggersOK = true;
}
if (objectFactoryPackage.startsWith(lp)) {
factoryOK = true;
}
if (logicEventsPackage.startsWith(lp)) {
eventsOK = true;
}
logicPackages[i] = lp;
}
if (!affectsOK) {
throw new RuntimeException("Affects impls package is not under one of the logic packages:" + affectsLogicPackage);
}
if (!triggersOK) {
throw new RuntimeException("Triggers impls package is not under one of the logic packages:" + triggersLogicPackage);
}
if (!factoryOK) {
throw new RuntimeException("Objects factories impls package is not under one of the logic packages:" + objectFactoryPackage);
}
if (!eventsOK) {
throw new RuntimeException("Codebase events impls package is not under one of the logic packages:" + logicEventsPackage);
}
logicClassesDir = props.get("pain.logic_classes_dir");
String userConsoleFactoryClassStr = props.get("pain.user_console_factory", false);
if (userConsoleFactoryClassStr == null) {
userConsoleFactoryClass = DefaultConsoleFactory.class;
} else {
userConsoleFactoryClass = Class.forName(userConsoleFactoryClassStr);
if (!ConsoleFactory.class.isAssignableFrom(userConsoleFactoryClass)) {
throw new RuntimeException("class ConsoleFactory is not assignable from :" + userConsoleFactoryClass.getName());
}
}
}
}
private static void prepareDefaultCodebaseDB(PainDB db) throws Exception {
CodebaseData data = new CodebaseData(db, Codebase.CODEBASE_VERSION);
Administrator admin = new Administrator(db);
admin.setLogin("root");
admin.setPassword("root");
data.getAdmins().put(admin.getLogin(), admin);
db.setRoot(data);
db.flush();
}
}