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(); } }