001/* 002 * Stallion Core: A Modern Web Framework 003 * 004 * Copyright (C) 2015 - 2016 Stallion Software LLC. 005 * 006 * This program is free software: you can redistribute it and/or modify it under the terms of the 007 * GNU General Public License as published by the Free Software Foundation, either version 2 of 008 * the License, or (at your option) any later version. This program is distributed in the hope that 009 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 011 * License for more details. You should have received a copy of the GNU General Public License 012 * along with this program. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>. 013 * 014 * 015 * 016 */ 017package io.stallion.boot; 018 019import io.stallion.assets.AssetsController; 020import io.stallion.asyncTasks.AsyncCoordinator; 021import io.stallion.asyncTasks.SimpleAsyncRunner; 022 023import io.stallion.dataAccess.DataAccessRegistry; 024import io.stallion.dataAccess.db.DB; 025import io.stallion.dataAccess.Tickets; 026import io.stallion.dataAccess.file.ListingEndpoints; 027import io.stallion.dataAccess.file.ListingExporter; 028import io.stallion.dataAccess.file.TextItemController; 029import io.stallion.dataAccess.filtering.FilterCache; 030import io.stallion.exceptions.CommandException; 031import io.stallion.exceptions.UsageException; 032import io.stallion.fileSystem.FileSystemWatcherRunner; 033import io.stallion.fileSystem.FileSystemWatcherService; 034import io.stallion.forms.SimpleFormTag; 035import io.stallion.hooks.HookRegistry; 036import io.stallion.jobs.JobCoordinator; 037import io.stallion.jobs.JobStatusController; 038import io.stallion.monitoring.HealthTracker; 039import io.stallion.plugins.StallionJavaPlugin; 040import io.stallion.plugins.PluginRegistry; 041import io.stallion.reflection.PropertyUtils; 042import io.stallion.requests.RoutesRegistry; 043import io.stallion.services.*; 044import io.stallion.sitemaps.SiteMapController; 045import io.stallion.restfulEndpoints.EndpointsRegistry; 046import io.stallion.restfulEndpoints.SlugRegistry; 047import io.stallion.settings.Settings; 048import io.stallion.requests.RequestHandler; 049import io.stallion.templating.JinjaTemplating; 050import io.stallion.templating.TemplateRenderer; 051import io.stallion.users.User; 052import io.stallion.users.UserController; 053import io.stallion.users.UsersApiResource; 054 055import java.io.File; 056 057import static io.stallion.utils.Literals.*; 058 059/** 060 * Helper methods for loading all the settings and services we need 061 * to run Stallion 062 */ 063public class AppContextLoader { 064 065 private static AppContextLoader _app; 066 067 public static boolean isNull() { 068 return _app == null; 069 } 070 071 public static AppContextLoader instance() { 072 if (_app == null) { 073 throw new UsageException("App instance is null. You must call a creation method before you can access the app instance."); 074 } 075 return _app; 076 } 077 078 /** 079 * Instaniates the _app singleton but only hydrates the settings. 080 * Does not load any of the controllers or anything else. 081 * @param options 082 * @return 083 */ 084 public static AppContextLoader loadWithSettingsOnly(CommandOptionsBase options) { 085 if (_app == null) { 086 _app = new AppContextLoader(); 087 } 088 File confFile = new File(options.getTargetPath() + "/conf/stallion.toml"); 089 if (!confFile.exists()) { 090 throw new CommandException("Cannot load site because the 'conf/stallion.toml' file is missing. You either targeted the wrong directory, or need to create a site first. Stallion expected to find the conf file at " + confFile.getAbsolutePath()); 091 } 092 093 Settings.init(options.getEnv(), options); 094 095 Settings.instance().setDevMode(options.isDevMode()); 096 097 if (empty(options.getLogLevel())) { 098 Log.setLogLevelFromSettings(); 099 } 100 if (Settings.instance().getLogToFile()) { 101 Log.enableFileLogger(); 102 } 103 104 Log.setAlwaysIncludeLineNumber(options.isLoggingAlwaysIncludesLineNumber()); 105 106 if (!Settings.instance().getLogToConsole()) { 107 Log.disableConsoleHandler(); 108 } 109 return _app; 110 } 111 112 /** 113 * Calls load completely as non-script, in non-test mode 114 * @param options 115 * @return 116 */ 117 public static AppContextLoader loadCompletely(CommandOptionsBase options) { 118 return loadCompletely(options, false); 119 } 120 121 /** 122 * Calls load completely in script mode, in non-test mode 123 * @param options 124 * @return 125 */ 126 public static AppContextLoader loadCompletelyForScript(CommandOptionsBase options) { 127 return loadCompletely(options, true); 128 } 129 130 /** 131 * Calls load completely in non-test mode 132 * @param options 133 * @return 134 */ 135 public static AppContextLoader loadCompletely(CommandOptionsBase options, boolean isScript) { 136 return loadCompletely(options, isScript, false); 137 } 138 139 /** 140 * Loads the entire Stallion application. 141 * 142 * First, it loads all registries -- the SlugRegistry, the EndpointRegistry, and the FileSystemWatcherService 143 * Second, it loads the database, caching layer, data access layer, assets, template engine 144 * Third, it loads builtin controllers and endpoints -- the UserController, the AdminEndpoints 145 * Fourth, it loads the JobRunner and the AsyncRunners 146 * 147 * It does not start any the job or async services -- it only loads them. 148 * 149 * @param options 150 * @param isScript 151 * @param testMode 152 * @return 153 */ 154 public static AppContextLoader loadCompletely(CommandOptionsBase options, boolean isScript, boolean testMode) { 155 if (_app == null || Settings.isNull()) { 156 loadWithSettingsOnly(options); 157 } 158 159 // Load registries 160 SiteMapController.load(); 161 FileSystemWatcherService.load(); 162 HookRegistry.load(); 163 SlugRegistry.load(); 164 EndpointsRegistry.load(); 165 166 167 // Data sources 168 DB.load(); 169 FilterCache.load(); 170 DataAccessRegistry.load(); 171 AssetsController.load(); 172 //DefinedBundle.load(); 173 TemplateRenderer.load(); 174 TemplateRenderer.instance().getJinjaTemplating().registerTag(new SimpleFormTag()); 175 RoutesRegistry.load(); 176 177 // Load users, admin and other default functionality 178 TransactionLogController.register(); 179 AuditTrailController.register(); 180 UserController.load(); 181 UsersApiResource.register(); 182 ListingEndpoints.register(); 183 ListingExporter.register(); 184 JobStatusController.selfRegister(); 185 186 SimpleAsyncRunner.load(); 187 188 if (testMode) { 189 AsyncCoordinator.initEphemeralSynchronousForTests(); 190 } else { 191 if (options instanceof ServeCommandOptions && ((ServeCommandOptions) options).isNoTasks()) { 192 193 } else { 194 AsyncCoordinator.init(); 195 } 196 } 197 198 // Load plugins 199 PluginRegistry.instance().bootJarPlugins(); 200 PluginRegistry.instance().loadAndRunJavascriptPlugins(true); 201 202 return _app; 203 } 204 205 /** 206 * Same as calling loadCompletely() and startServicesForTests(), but configures in a way suitable for tests 207 * Some services, such as jobs, will not be started. 208 * It will run asyncTasks in ephemeral synchronous mode, rather than the normal background thread mode 209 * 210 * @param targetFolder 211 * @return 212 */ 213 public static AppContextLoader loadAndStartForTests(String targetFolder) { 214 CommandOptionsBase options = new CommandOptionsBase(); 215 options.setEnv(or(System.getProperty("stallionEnv"), "test")); 216 options.setTargetPath(targetFolder); 217 return loadAndStartForTests(options); 218 } 219 220 /** 221 * Same as calling loadCompletely() and startServicesForTests(), but configures in a way suitable for tests 222 * Some services, such as jobs, will not be started. 223 * It will run asyncTasks in ephemeral synchronous mode, rather than the normal background thread mode 224 * 225 * @param options 226 * @return 227 */ 228 public static AppContextLoader loadAndStartForTests(CommandOptionsBase options) { 229 PluginRegistry.loadWithJavaPlugins(options.getTargetPath()); 230 loadCompletely(options, false, true); 231 _app.startServicesForTests(); 232 SimpleAsyncRunner.setSyncMode(true); 233 return _app; 234 } 235 236 /** 237 * Shutsdown all services, destroys all singletons, de-loads everything from memory. 238 */ 239 public static void shutdown() { 240 // This should be roughly the opposite order of loading 241 242 AsyncCoordinator.gracefulShutdown(); 243 JobCoordinator.shutdown(); 244 FileSystemWatcherService .shutdown(); 245 246 PluginRegistry.shutdown(); 247 248 //DefinedBundle.shutdown(); 249 AssetsController.shutdown(); 250 TemplateRenderer.shutdown(); 251 252 RoutesRegistry.shutdown(); 253 254 DataAccessRegistry.shutdown(); 255 SiteMapController.shutdown(); 256 257 DB.shutdown(); 258 259 HookRegistry.shutdown(); 260 EndpointsRegistry.shutdown(); 261 SimpleAsyncRunner.shutdown(); 262 FilterCache.shutdown(); 263 HealthTracker.shutdown(); 264 LocalMemoryCache.shutdown(); 265 266 PropertyUtils.resetCache(); 267 268 269 270 _app = null; 271 } 272 273 /** 274 * Starts all services 275 * 276 * - starts the async task processing 277 * - starts the job runner 278 * - starts watching the file system for changes 279 * - etc. 280 * 281 * @return 282 */ 283 public AppContextLoader startAllServices() { 284 if (AsyncCoordinator.instance() != null) { 285 AsyncCoordinator.startup(); 286 } 287 JobCoordinator.startUp(); 288 FileSystemWatcherService.start(); 289 HealthTracker.start(); 290 for (StallionJavaPlugin plugin: PluginRegistry.instance().getJavaPluginByName().values()) { 291 plugin.startServices(); 292 } 293 FilterCache.start(); 294 LocalMemoryCache.start(); 295 return _app; 296 } 297 298 /** 299 * Starts only the services that are applicable for doing integration tests. 300 * Some services, such as jobs, will not be started 301 * AsyncTasks in ephemeral synchronous mode, rather than the normal background thread mode 302 * @return 303 */ 304 public AppContextLoader startServicesForTests() { 305 for (StallionJavaPlugin plugin: PluginRegistry.instance().getJavaPluginByName().values()) { 306 plugin.startServicesForTests(); 307 } 308 FilterCache.start(); 309 LocalMemoryCache.start(); 310 311 return _app; 312 } 313 314 @Deprecated 315 public Class loadClass(String className) { 316 Class cls = null; 317 try { 318 cls = getClass().getClassLoader().loadClass(className); 319 return cls; 320 } catch (ClassNotFoundException e) { 321 322 } 323 for (StallionJavaPlugin booter: PluginRegistry.instance().getJavaPluginByName().values()) { 324 try { 325 cls = getClass().getClassLoader().loadClass(className); 326 return cls; 327 } catch (ClassNotFoundException e) { 328 329 } 330 } 331 return null; 332 } 333 334 @Deprecated 335 public Class loadClass(String pluginName, String className) { 336 try { 337 Class cls = PluginRegistry.instance().getJavaPluginByName().get(pluginName).getClass().getClassLoader().loadClass(className); 338 return cls; 339 } catch (ClassNotFoundException e) { 340 throw new RuntimeException(e); 341 } 342 } 343 344 @Deprecated 345 public String getEnv() { 346 return Settings.instance().getEnv(); 347 } 348 349 350 @Deprecated 351 public String getTargetFolder() { 352 return Settings.instance().getTargetFolder(); 353 } 354 355 356 @Deprecated 357 public TextItemController getPageController() { 358 return DataAccessRegistry.instance().getPages(); 359 } 360 361 362 @Deprecated 363 public TextItemController getPostController() { 364 return DataAccessRegistry.instance().getPosts(); 365 } 366 367 @Deprecated 368 public Settings getSettings() { 369 return Settings.instance(); 370 } 371 372 373 @Deprecated 374 public DataAccessRegistry getDal() { 375 return DataAccessRegistry.instance(); 376 } 377 378 @Deprecated 379 public DataAccessRegistry dal() { 380 return DataAccessRegistry.instance(); 381 } 382 383 @Deprecated 384 public Integer getPort() { 385 return Settings.instance().getPort(); 386 } 387 388 389 @Deprecated 390 public RequestHandler getHandler() { 391 return RequestHandler.instance(); 392 } 393 394 @Deprecated 395 public FileSystemWatcherRunner getFileSystemWatcherRunner() { 396 return FileSystemWatcherService.instance(); 397 } 398 399 @Deprecated 400 public TemplateRenderer getTemplateRenderer() { 401 return TemplateRenderer.instance(); 402 } 403 404 @Deprecated 405 public AssetsController getAssetsController() { 406 return AssetsController.instance(); 407 } 408 409 @Deprecated 410 public SlugRegistry getSlugRegistry() { 411 return SlugRegistry.instance(); 412 } 413 414 @Deprecated 415 public UserController<User> getUserController() { 416 return (UserController)dal().get("users"); 417 } 418 419 @Deprecated 420 public Tickets getTickets() { 421 return DataAccessRegistry.instance().getTickets(); 422 } 423 424 @Deprecated 425 public PluginRegistry getPluginRegistry() { 426 return PluginRegistry.instance(); 427 } 428 429 @Deprecated 430 public JinjaTemplating getJinjaTemplating() { 431 return TemplateRenderer.instance().getJinjaTemplating(); 432 } 433 434 435}