Coordinator.php 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
  5. *
  6. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  7. * @author Joas Schilling <coding@schilljs.com>
  8. * @author Julius Härtl <jus@bitgrid.net>
  9. * @author Morris Jobke <hey@morrisjobke.de>
  10. * @author Robin Appelman <robin@icewind.nl>
  11. *
  12. * @license GNU AGPL version 3 or any later version
  13. *
  14. * This program is free software: you can redistribute it and/or modify
  15. * it under the terms of the GNU Affero General Public License as
  16. * published by the Free Software Foundation, either version 3 of the
  17. * License, or (at your option) any later version.
  18. *
  19. * This program is distributed in the hope that it will be useful,
  20. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  22. * GNU Affero General Public License for more details.
  23. *
  24. * You should have received a copy of the GNU Affero General Public License
  25. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  26. *
  27. */
  28. namespace OC\AppFramework\Bootstrap;
  29. use OCP\Diagnostics\IEventLogger;
  30. use function class_exists;
  31. use function class_implements;
  32. use function in_array;
  33. use OC_App;
  34. use OC\Support\CrashReport\Registry;
  35. use OCP\AppFramework\App;
  36. use OCP\AppFramework\Bootstrap\IBootstrap;
  37. use OCP\AppFramework\QueryException;
  38. use OCP\Dashboard\IManager;
  39. use OCP\EventDispatcher\IEventDispatcher;
  40. use OCP\IServerContainer;
  41. use Psr\Log\LoggerInterface;
  42. use Throwable;
  43. class Coordinator {
  44. /** @var IServerContainer */
  45. private $serverContainer;
  46. /** @var Registry */
  47. private $registry;
  48. /** @var IManager */
  49. private $dashboardManager;
  50. /** @var IEventDispatcher */
  51. private $eventDispatcher;
  52. /** @var IEventLogger */
  53. private $eventLogger;
  54. /** @var LoggerInterface */
  55. private $logger;
  56. /** @var RegistrationContext|null */
  57. private $registrationContext;
  58. /** @var string[] */
  59. private $bootedApps = [];
  60. public function __construct(
  61. IServerContainer $container,
  62. Registry $registry,
  63. IManager $dashboardManager,
  64. IEventDispatcher $eventListener,
  65. IEventLogger $eventLogger,
  66. LoggerInterface $logger
  67. ) {
  68. $this->serverContainer = $container;
  69. $this->registry = $registry;
  70. $this->dashboardManager = $dashboardManager;
  71. $this->eventDispatcher = $eventListener;
  72. $this->eventLogger = $eventLogger;
  73. $this->logger = $logger;
  74. }
  75. public function runInitialRegistration(): void {
  76. $this->registerApps(OC_App::getEnabledApps());
  77. }
  78. public function runLazyRegistration(string $appId): void {
  79. $this->registerApps([$appId]);
  80. }
  81. /**
  82. * @param string[] $appIds
  83. */
  84. private function registerApps(array $appIds): void {
  85. $this->eventLogger->start('bootstrap:register_apps', '');
  86. if ($this->registrationContext === null) {
  87. $this->registrationContext = new RegistrationContext($this->logger);
  88. }
  89. $apps = [];
  90. foreach ($appIds as $appId) {
  91. $this->eventLogger->start("bootstrap:register_app:$appId", "Register $appId");
  92. $this->eventLogger->start("bootstrap:register_app:$appId:autoloader", "Setup autoloader for $appId");
  93. /*
  94. * First, we have to enable the app's autoloader
  95. *
  96. * @todo use $this->appManager->getAppPath($appId) here
  97. */
  98. $path = OC_App::getAppPath($appId);
  99. if ($path === false) {
  100. // Ignore
  101. continue;
  102. }
  103. OC_App::registerAutoloading($appId, $path);
  104. $this->eventLogger->end("bootstrap:register_app:$appId:autoloader");
  105. /*
  106. * Next we check if there is an application class, and it implements
  107. * the \OCP\AppFramework\Bootstrap\IBootstrap interface
  108. */
  109. $appNameSpace = App::buildAppNamespace($appId);
  110. $applicationClassName = $appNameSpace . '\\AppInfo\\Application';
  111. try {
  112. if (class_exists($applicationClassName) && in_array(IBootstrap::class, class_implements($applicationClassName), true)) {
  113. $this->eventLogger->start("bootstrap:register_app:$appId:application", "Load `Application` instance for $appId");
  114. try {
  115. /** @var IBootstrap|App $application */
  116. $apps[$appId] = $application = $this->serverContainer->query($applicationClassName);
  117. } catch (QueryException $e) {
  118. // Weird, but ok
  119. $this->eventLogger->end("bootstrap:register_app:$appId");
  120. continue;
  121. }
  122. $this->eventLogger->end("bootstrap:register_app:$appId:application");
  123. $this->eventLogger->start("bootstrap:register_app:$appId:register", "`Application::register` for $appId");
  124. $application->register($this->registrationContext->for($appId));
  125. $this->eventLogger->end("bootstrap:register_app:$appId:register");
  126. }
  127. } catch (Throwable $e) {
  128. $this->logger->emergency('Error during app service registration: ' . $e->getMessage(), [
  129. 'exception' => $e,
  130. 'app' => $appId,
  131. ]);
  132. $this->eventLogger->end("bootstrap:register_app:$appId");
  133. continue;
  134. }
  135. $this->eventLogger->end("bootstrap:register_app:$appId");
  136. }
  137. $this->eventLogger->start('bootstrap:register_apps:apply', 'Apply all the registered service by apps');
  138. /**
  139. * Now that all register methods have been called, we can delegate the registrations
  140. * to the actual services
  141. */
  142. $this->registrationContext->delegateCapabilityRegistrations($apps);
  143. $this->registrationContext->delegateCrashReporterRegistrations($apps, $this->registry);
  144. $this->registrationContext->delegateDashboardPanelRegistrations($this->dashboardManager);
  145. $this->registrationContext->delegateEventListenerRegistrations($this->eventDispatcher);
  146. $this->registrationContext->delegateContainerRegistrations($apps);
  147. $this->eventLogger->end('bootstrap:register_apps:apply');
  148. $this->eventLogger->end('bootstrap:register_apps');
  149. }
  150. public function getRegistrationContext(): ?RegistrationContext {
  151. return $this->registrationContext;
  152. }
  153. public function bootApp(string $appId): void {
  154. if (isset($this->bootedApps[$appId])) {
  155. return;
  156. }
  157. $this->bootedApps[$appId] = true;
  158. $appNameSpace = App::buildAppNamespace($appId);
  159. $applicationClassName = $appNameSpace . '\\AppInfo\\Application';
  160. if (!class_exists($applicationClassName)) {
  161. // Nothing to boot
  162. return;
  163. }
  164. /*
  165. * Now it is time to fetch an instance of the App class. For classes
  166. * that implement \OCP\AppFramework\Bootstrap\IBootstrap this means
  167. * the instance was already created for register, but any other
  168. * (legacy) code will now do their magic via the constructor.
  169. */
  170. $this->eventLogger->start('bootstrap:boot_app:' . $appId, "Call `Application::boot` for $appId");
  171. try {
  172. /** @var App $application */
  173. $application = $this->serverContainer->query($applicationClassName);
  174. if ($application instanceof IBootstrap) {
  175. /** @var BootContext $context */
  176. $context = new BootContext($application->getContainer());
  177. $application->boot($context);
  178. }
  179. } catch (QueryException $e) {
  180. $this->logger->error("Could not boot $appId: " . $e->getMessage(), [
  181. 'exception' => $e,
  182. ]);
  183. } catch (Throwable $e) {
  184. $this->logger->emergency("Could not boot $appId: " . $e->getMessage(), [
  185. 'exception' => $e,
  186. ]);
  187. }
  188. $this->eventLogger->end('bootstrap:boot_app:' . $appId);
  189. }
  190. public function isBootable(string $appId) {
  191. $appNameSpace = App::buildAppNamespace($appId);
  192. $applicationClassName = $appNameSpace . '\\AppInfo\\Application';
  193. return class_exists($applicationClassName) &&
  194. in_array(IBootstrap::class, class_implements($applicationClassName), true);
  195. }
  196. }