1 /** 2 * The module implements application skeleton 3 * 4 * Copyright: (c) 2015-2017, Milofon Project. 5 * License: Subject to the terms of the BSD license, as written in the included LICENSE.txt file. 6 * Authors: Maksim Galanin 7 */ 8 module dango.system.application; 9 10 public 11 { 12 import BrightProof : SemVer; 13 import proped : Properties, Loader; 14 import proped.loader : createPropertiesLoader; 15 16 import poodinis : DependencyContainer, existingInstance; 17 18 import vibe.core.log : registerLogger, logInfo, logDiagnostic; 19 import vibe.core.core : runEventLoop, lowerPrivileges, runTask, Task; 20 21 import dango.system.commandline : CommandLineProcessor; 22 import dango.system.properties : PropertiesProxy; 23 } 24 25 private 26 { 27 import std.array : empty; 28 29 import dango.system.properties : createLoaderFromContainer, PropertiesContext; 30 import dango.system.logging : configureLogging, LoggingContext; 31 } 32 33 34 /** 35 * Интерфейс приложения 36 */ 37 interface Application 38 { 39 /** 40 * Запуск приложения 41 * 42 * Params: 43 * args = Входящие параметры 44 * 45 * Returns: Код завершения работы приложения 46 */ 47 int run(string[] args); 48 49 /** 50 * Функция загружает свойства из файла при помощи локального загрузчика 51 * Params: 52 * 53 * filePath = Путь до файла 54 * 55 * Returns: Объект свойств 56 */ 57 Properties loadProperties(string filePath); 58 59 /** 60 * Свойство возвращает наименование приложения 61 */ 62 string name() @property pure nothrow; 63 64 /** 65 * Свойство возвращает версию приложения 66 */ 67 SemVer release() @property pure nothrow; 68 } 69 70 71 /** 72 * Базовый класс приложения 73 */ 74 abstract class BaseApplication : Application 75 { 76 private 77 { 78 string _applicationName; 79 SemVer _applicationVersion; 80 shared(DependencyContainer) _container; 81 string[] _configFiles; 82 Loader _propLoader; 83 } 84 85 86 this(string name, string _version) 87 { 88 this(name, SemVer(_version)); 89 } 90 91 92 this(string name, SemVer _version) 93 { 94 _applicationName = name; 95 _applicationVersion = _version; 96 } 97 98 /** 99 * See_Also: Application.run 100 */ 101 final int run(string[] args) 102 { 103 // иницмализируем зависимости 104 _container = new shared(DependencyContainer)(); 105 _propLoader = createPropertiesLoader(); 106 107 // загружаем параметры командной строки 108 auto cProcessor = new CommandLineProcessor(args); 109 if (!doParseCommandLine(cProcessor)) 110 return 1; 111 112 if (_configFiles.empty) 113 _configFiles = getDefaultConfigFiles(); 114 115 Properties config; 116 117 foreach(string cFile; _configFiles) 118 config ~= loadProperties(cFile); 119 120 config ~= cProcessor.getOptionProperties(); 121 config ~= cProcessor.getEnvironmentProperties(); 122 123 doInitDependencies(_container, config); 124 125 configureLogging(container, config, ®isterLogger); 126 127 initDependencies(container, config); 128 129 return runApplication(config); 130 } 131 132 /** 133 * Свойство возвращает локальный контейнер 134 */ 135 shared(DependencyContainer) container() @property pure nothrow 136 { 137 return _container; 138 } 139 140 /** 141 * Свойство возвращает наименование приложения 142 */ 143 string name() @property pure nothrow 144 { 145 return _applicationName; 146 } 147 148 /** 149 * Свойство возвращает версию приложения 150 */ 151 SemVer release() @property pure nothrow 152 { 153 return _applicationVersion; 154 } 155 156 /** 157 * Функция загружает свойства из файла при помощи локального загрузчика 158 */ 159 Properties loadProperties(string filePath) 160 { 161 if (_propLoader is null) 162 _propLoader = createPropertiesLoader(); 163 return _propLoader(filePath); 164 } 165 166 protected: 167 168 /** 169 * Запуск приложения 170 * 171 * Params: 172 * config = Входящие параметры 173 * 174 * Returns: Код завершения работы приложения 175 */ 176 int runApplication(Properties config); 177 178 /** 179 * Возвращает пути до файлов по-умолчанию 180 */ 181 string[] getDefaultConfigFiles() 182 { 183 return []; 184 } 185 186 /** 187 * Инициализация зависимостей 188 * 189 * Params: 190 * container = Контейнер DI 191 * 192 * Example: 193 * --- 194 * initDependencies(container); 195 * --- 196 */ 197 void initDependencies(shared(DependencyContainer) container, Properties config) 198 { 199 } 200 201 /** 202 * Получение аргументов из командной строки 203 * 204 * Params: 205 * processor = Объект для разбора командной строки 206 * 207 * Returns: Успешность получения свойств 208 */ 209 bool parseCommandLine(CommandLineProcessor processor) 210 { 211 return true; 212 } 213 214 /** 215 * Возвращает строку помощи для консоли 216 */ 217 string helpText() @property 218 { 219 return _applicationName; 220 } 221 222 private: 223 224 void doInitDependencies(shared(DependencyContainer) container, Properties config) 225 { 226 container.registerContext!PropertiesContext; 227 container.registerContext!LoggingContext; 228 container.register!(Application, typeof(this)).existingInstance(this); 229 container.register!PropertiesProxy.existingInstance(new PropertiesProxy(config)); 230 } 231 232 233 bool doParseCommandLine(CommandLineProcessor processor) 234 { 235 processor.readOption("config|c", &_configFiles, "Конфигурационный файл"); 236 237 bool ret = parseCommandLine(processor); 238 ret &= processor.checkOptions(); 239 240 if (!ret) 241 processor.printer(helpText); 242 243 return ret; 244 } 245 } 246 247 248 /** 249 * Приложение запускающее обработчик событий 250 * работающее в режиме демона 251 */ 252 abstract class DaemonApplication : BaseApplication 253 { 254 this(string name, string _version) 255 { 256 super(name, _version); 257 } 258 259 260 this(string name, SemVer _version) 261 { 262 super(name, _version); 263 } 264 265 protected: 266 267 override final int runApplication(Properties config) 268 { 269 return runLoop(config); 270 } 271 272 /** 273 * Запуск демона сервисов 274 * Params: 275 * 276 * config = Конфигурация приложения 277 */ 278 void initializeDaemon(Properties config); 279 280 /** 281 * Остановка демона сервисов 282 * Params: 283 * 284 * exitStatus = Код завершения приложения 285 */ 286 int finalizeDaemon(int exitStatus); 287 288 private: 289 290 /** 291 * Запуск основного цикла обработки событий 292 * Params: 293 * 294 * config = Конфигурация приложения 295 */ 296 int runLoop(Properties config) 297 { 298 logInfo("Запуск приложения %s (%s)", name, release); 299 300 lowerPrivileges(); 301 302 initializeDaemon(config); 303 304 logDiagnostic("Запуск цикла обработки событий..."); 305 int status = runEventLoop(); 306 logDiagnostic("Цикл событий зaвершен со статутом %d.", status); 307 308 return finalizeDaemon(status); 309 } 310 } 311