hydranode.h

Go to the documentation of this file.
00001 /**
00002  *  Copyright (C) 2004-2005 Alo Sarv <madcat_@users.sourceforge.net>
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017  */
00018 
00019 /** @file hydranode.h Main application header file */
00020 
00021 #ifndef __HYDRANODE_H__
00022 #define __HYDRANODE_H__
00023 
00024 /**
00025  * \mainpage
00026  *
00027  * HydraNode Core - Modular Peer-to-peer client framework
00028  * \date   August 2004
00029  * \author Alo Sarv <madcat_ (at) users (dot) sourceforge (dot) net>
00030  *
00031  * \section maintainers Notes to Core Framework Maintainers
00032  *
00033  * As is known, programmers greatest enemy is second programmer. During
00034  * maintainance phase of a codebase, it is very easy to make the codebase
00035  * into <i>spaghetty</i> code. In order to prevent that, I have attempted to
00036  * write the initial implementation as safely as possible to keep the codebase
00037  * from falling apart as soon as tens of maintainers jump into it. Here are
00038  * some things you should keep in mind:
00039  *
00040  * - Coding Standard. I have used a very strict coding / documenting style in
00041  *   the codebase, and I'd prefer if it stayed that way. It is very important
00042  *   to keep the style same all over the codebase, as well as to keep
00043  *   documentation up to date. So if you modify a function, verify that the
00044  *   function's documentation reflects the new behaviour (do so even during
00045  *   trivial changes). Likewise, most files have "overview" documentation
00046  *   section at the top (headers). Review that on regular basis to keep that
00047  *   up to date. Now all this might sound very time-consuming and boring, but
00048  *   trust me - it pays off in long-term. I know you hate documenting, I did too
00049  *   until I had to maintain 100'000-line codebase with absolutely no
00050  *   documentation.
00051  *   <br><b>Follow the coding standard rules. Fully document all your code. Keep
00052  *   the documentation up to date.</b>
00053  * - Objects Decoupling. I have went through long sleepless nights in order
00054  *   to design the subsystems so that they would be decoupled maximally from
00055  *   each other. Systems communicate with each other at VERY MINIMAL base.
00056  *   Contained objects DO NOT know about the containers (e.g. MetaData and
00057  *   MetaDb, SharedFile and FilesList). The order of class declarations in
00058  *   headers reflects that idea - contained classes are declared prior to
00059  *   containers, so there won't be any chances they could 'accidentally' call
00060  *   container's functions (this last one applies only to inline functions in
00061  *   headers tho, you could still call the Container's functions in source
00062  *   files). If you really need to pass information from Containable to
00063  *   Container, use event tables (as I have done with MetaData and MetaDb).
00064  *   In short - if you REALLY need to pass messages over systems which, by
00065  *   common sense, shouldn't be aware of each other's existance, use event
00066  *   tables. They are the hammer of decoupling. Use them!
00067  *   <br><b>Do not couple together objects that are not supposed to know about
00068  *   each other!</b>
00069  * - Destructors. No, you can't do things in destructors. The reason for
00070  *   that is that most top-level container classes are Singletons and get
00071  *   destroyed during application shutdown <b>AFTER</b> exiting from main().
00072  *   And the order of destruction is undefined, so you CANNOT rely that
00073  *   any other systems are still up and running if you are in some destructor -
00074  *   it may be the application shutting down, and the other subsystems are
00075  *   already down.
00076  *   <br><b>Do not do anything except cleanup in destructors.</b>
00077  * - Testing Apps. Nearly each and every class and subsystem should have a
00078  *   corresponding test application, located in tests/ subdir, which tests the
00079  *   classes' or subsystems features and verifies it's integrity. These tests
00080  *   should immediately fail as soon as the target behaves differently than it
00081  *   should. ALWAYS run the corresponding tests after modifying the contents of
00082  *   a class or subsystem, no matter how trivial the change may sound. Also,
00083  *   when new bugs are found in subsystems, the FIRST step should be to update
00084  *   the corresponding test case to detect this bug, and only after that the
00085  *   bug itself be addressed. This ensures that codebase stays operational
00086  *   during maintainance, bugs mostly don't even make it to CVS, and old bugs
00087  *   do not return. Only exception here is cross-platform issues - not all
00088  *   developers have access to all supported platforms, so it's usually
00089  *   forgivable if the tests work on developer's platform, but fail on some
00090  *   other platform. But don't make this your habit - this means someone else
00091  *   must fix your broken code, and that some else isn't going to be happy about
00092  *   it. <br><b>Regularly regress-test your code. Write regress-tests for all
00093  *   new classes/subsystems. Keep regress-tests up-to-date with the codebase.
00094  *   </b><br>
00095  */
00096 
00097 #include <hn/event.h>                            // EventSink
00098 #include <hn/object.h>                           // Object
00099 #include <boost/filesystem/path.hpp>             // getConfigDir() return val
00100 
00101 enum HNEvent {
00102         EVT_EXIT = 0,
00103         EVT_SAVE_MDB   //! instructs to save MetaDb. Done every X minutes
00104 };
00105 
00106 /**
00107  * Main application class.
00108  */
00109 class DLLEXPORT HydraNode : public Object {
00110 public:
00111         DECLARE_EVENT_TABLE(HydraNode*, HNEvent);
00112 
00113         //! Returns the single instance of this Singleton
00114         static HydraNode& instance();
00115 
00116         /**
00117          * Initialize HydraNode core.
00118          *
00119          * @param argc        Number of arguments
00120          * @param argv        Arguments
00121          */
00122         void init(int argc, char **argv);
00123 
00124         /**
00125          * Runs the hydranode core, performing initialization, mainloop until
00126          * exit command is received, and then cleanup.
00127          *
00128          * @param argc        Number of arguments
00129          * @param argv        Arguments
00130          * @return            0 on clean shutdown, nonzero error code otherwise.
00131          */
00132         int run(int argc, char **argv);
00133 
00134         /**
00135          * Called on shutdown, this method performs all cleanup and saving all
00136          * settings.
00137          *
00138          * @return            0 on clean shutdown, nonzero error code otherwise.
00139          */
00140         int cleanup();
00141 
00142         /**
00143          * Main event loop. This method enters HydraNode main loop, calling
00144          * doLoop() until m_running is true, and then exits the application.
00145          *
00146          * @return            0 on clean exit, nonzero error code otherwise.
00147          */
00148         int mainLoop();
00149 
00150         /**
00151          * Perform a single event loop.
00152          */
00153         void doLoop();
00154 
00155         /**
00156          * Accessor, retrieves configuration directory
00157          *
00158          * @return   Current configuration directory
00159          */
00160         boost::filesystem::path getConfigDir() {
00161                 return m_confDir;
00162         }
00163 
00164         /**
00165          * Exit the application.
00166          */
00167         void exit() { m_running = false; }
00168 
00169         /**
00170          * Retrieve the application version. The return value bytes are set as
00171          * follows:
00172          * [null] [major] [minor] [patch]
00173          * Thus version 2.5.3 would return a 4-byte value with the following
00174          * content:
00175          * 0253
00176          */
00177         uint32_t getAppVer() const;
00178 
00179         /**
00180          * Get a human-readable application version string. The returned string
00181          * is something like this:
00182          * "HydraNode v0.4.6"
00183          */
00184         std::string getAppVerLong() const;
00185 
00186         //! Check if the application is running. This indicates whether the
00187         //! application is inside main loop, e.g. not starting up or shutting
00188         //! down.
00189         bool isRunning() const { return m_running; }
00190 private:
00191         //! @name Various initialization functions
00192         //@{
00193         void initConfig();
00194         void initLog();
00195         void initSockets();
00196         void initModules();
00197         void initFiles();
00198         //@}
00199 
00200         /**
00201          * Check for directories existance, and create it if neccesery.
00202          *
00203          * @param dir     Path to check/create.
00204          * @param alt     Alternative directory name to be used if smth fails
00205          * @return        The directory checked/created.
00206          *
00207          * If something goes terribly wrong in the directory checking/creation,
00208          * this method attempts to use a subdirectory from current working
00209          * directory. If that also fails, it will throw an exception. The return
00210          * value indicates the actual directory after all these checks, and may
00211          * differ from the original passed path.
00212          */
00213         boost::filesystem::path checkCreateDir(
00214                 boost::filesystem::path dir, const std::string &alt
00215         );
00216 
00217         //! Keeps track if we are running.
00218         bool m_running;
00219 
00220         //! Global configuration directory
00221         boost::filesystem::path m_confDir;
00222 
00223         //! Application binary build/modification date
00224         uint32_t m_buildDate;
00225 
00226         //! Event handler for our own events, used for some regular tasks
00227         void onEvent(HydraNode *hn, HNEvent evt);
00228 
00229         /** @name Singleton */
00230         //@{
00231         HydraNode();                                        //!< Forbidden
00232         HydraNode(const HydraNode&);                        //!< Forbidden
00233         HydraNode& operator=(HydraNode&);                   //!< Forbidden
00234         ~HydraNode();                                       //!< Forbidden
00235         //@}
00236 };
00237 
00238 #endif