You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Coordinator.php 6.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  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 OC\Support\CrashReport\Registry;
  30. use OC_App;
  31. use OCP\App\AppPathNotFoundException;
  32. use OCP\App\IAppManager;
  33. use OCP\AppFramework\App;
  34. use OCP\AppFramework\Bootstrap\IBootstrap;
  35. use OCP\AppFramework\QueryException;
  36. use OCP\Dashboard\IManager;
  37. use OCP\Diagnostics\IEventLogger;
  38. use OCP\EventDispatcher\IEventDispatcher;
  39. use OCP\IServerContainer;
  40. use Psr\Log\LoggerInterface;
  41. use Throwable;
  42. use function class_exists;
  43. use function class_implements;
  44. use function in_array;
  45. class Coordinator {
  46. /** @var RegistrationContext|null */
  47. private $registrationContext;
  48. /** @var string[] */
  49. private $bootedApps = [];
  50. public function __construct(
  51. private IServerContainer $serverContainer,
  52. private Registry $registry,
  53. private IManager $dashboardManager,
  54. private IEventDispatcher $eventDispatcher,
  55. private IEventLogger $eventLogger,
  56. private IAppManager $appManager,
  57. private LoggerInterface $logger,
  58. ) {
  59. }
  60. public function runInitialRegistration(): void {
  61. $this->registerApps(OC_App::getEnabledApps());
  62. }
  63. public function runLazyRegistration(string $appId): void {
  64. $this->registerApps([$appId]);
  65. }
  66. /**
  67. * @param string[] $appIds
  68. */
  69. private function registerApps(array $appIds): void {
  70. $this->eventLogger->start('bootstrap:register_apps', '');
  71. if ($this->registrationContext === null) {
  72. $this->registrationContext = new RegistrationContext($this->logger);
  73. }
  74. $apps = [];
  75. foreach ($appIds as $appId) {
  76. $this->eventLogger->start("bootstrap:register_app:$appId", "Register $appId");
  77. $this->eventLogger->start("bootstrap:register_app:$appId:autoloader", "Setup autoloader for $appId");
  78. /*
  79. * First, we have to enable the app's autoloader
  80. */
  81. try {
  82. $path = $this->appManager->getAppPath($appId);
  83. } catch (AppPathNotFoundException) {
  84. // Ignore
  85. continue;
  86. }
  87. OC_App::registerAutoloading($appId, $path);
  88. $this->eventLogger->end("bootstrap:register_app:$appId:autoloader");
  89. /*
  90. * Next we check if there is an application class, and it implements
  91. * the \OCP\AppFramework\Bootstrap\IBootstrap interface
  92. */
  93. $appNameSpace = App::buildAppNamespace($appId);
  94. $applicationClassName = $appNameSpace . '\\AppInfo\\Application';
  95. try {
  96. if (class_exists($applicationClassName) && in_array(IBootstrap::class, class_implements($applicationClassName), true)) {
  97. $this->eventLogger->start("bootstrap:register_app:$appId:application", "Load `Application` instance for $appId");
  98. try {
  99. /** @var IBootstrap|App $application */
  100. $apps[$appId] = $application = $this->serverContainer->query($applicationClassName);
  101. } catch (QueryException $e) {
  102. // Weird, but ok
  103. $this->eventLogger->end("bootstrap:register_app:$appId");
  104. continue;
  105. }
  106. $this->eventLogger->end("bootstrap:register_app:$appId:application");
  107. $this->eventLogger->start("bootstrap:register_app:$appId:register", "`Application::register` for $appId");
  108. $application->register($this->registrationContext->for($appId));
  109. $this->eventLogger->end("bootstrap:register_app:$appId:register");
  110. }
  111. } catch (Throwable $e) {
  112. $this->logger->emergency('Error during app service registration: ' . $e->getMessage(), [
  113. 'exception' => $e,
  114. 'app' => $appId,
  115. ]);
  116. $this->eventLogger->end("bootstrap:register_app:$appId");
  117. continue;
  118. }
  119. $this->eventLogger->end("bootstrap:register_app:$appId");
  120. }
  121. $this->eventLogger->start('bootstrap:register_apps:apply', 'Apply all the registered service by apps');
  122. /**
  123. * Now that all register methods have been called, we can delegate the registrations
  124. * to the actual services
  125. */
  126. $this->registrationContext->delegateCapabilityRegistrations($apps);
  127. $this->registrationContext->delegateCrashReporterRegistrations($apps, $this->registry);
  128. $this->registrationContext->delegateDashboardPanelRegistrations($this->dashboardManager);
  129. $this->registrationContext->delegateEventListenerRegistrations($this->eventDispatcher);
  130. $this->registrationContext->delegateContainerRegistrations($apps);
  131. $this->eventLogger->end('bootstrap:register_apps:apply');
  132. $this->eventLogger->end('bootstrap:register_apps');
  133. }
  134. public function getRegistrationContext(): ?RegistrationContext {
  135. return $this->registrationContext;
  136. }
  137. public function bootApp(string $appId): void {
  138. if (isset($this->bootedApps[$appId])) {
  139. return;
  140. }
  141. $this->bootedApps[$appId] = true;
  142. $appNameSpace = App::buildAppNamespace($appId);
  143. $applicationClassName = $appNameSpace . '\\AppInfo\\Application';
  144. if (!class_exists($applicationClassName)) {
  145. // Nothing to boot
  146. return;
  147. }
  148. /*
  149. * Now it is time to fetch an instance of the App class. For classes
  150. * that implement \OCP\AppFramework\Bootstrap\IBootstrap this means
  151. * the instance was already created for register, but any other
  152. * (legacy) code will now do their magic via the constructor.
  153. */
  154. $this->eventLogger->start('bootstrap:boot_app:' . $appId, "Call `Application::boot` for $appId");
  155. try {
  156. /** @var App $application */
  157. $application = $this->serverContainer->query($applicationClassName);
  158. if ($application instanceof IBootstrap) {
  159. /** @var BootContext $context */
  160. $context = new BootContext($application->getContainer());
  161. $application->boot($context);
  162. }
  163. } catch (QueryException $e) {
  164. $this->logger->error("Could not boot $appId: " . $e->getMessage(), [
  165. 'exception' => $e,
  166. ]);
  167. } catch (Throwable $e) {
  168. $this->logger->emergency("Could not boot $appId: " . $e->getMessage(), [
  169. 'exception' => $e,
  170. ]);
  171. }
  172. $this->eventLogger->end('bootstrap:boot_app:' . $appId);
  173. }
  174. public function isBootable(string $appId) {
  175. $appNameSpace = App::buildAppNamespace($appId);
  176. $applicationClassName = $appNameSpace . '\\AppInfo\\Application';
  177. return class_exists($applicationClassName) &&
  178. in_array(IBootstrap::class, class_implements($applicationClassName), true);
  179. }
  180. }