Coordinator.php 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  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. if ($this->registrationContext === null) {
  86. $this->registrationContext = new RegistrationContext($this->logger);
  87. }
  88. $apps = [];
  89. foreach ($appIds as $appId) {
  90. /*
  91. * First, we have to enable the app's autoloader
  92. *
  93. * @todo use $this->appManager->getAppPath($appId) here
  94. */
  95. $path = OC_App::getAppPath($appId);
  96. if ($path === false) {
  97. // Ignore
  98. continue;
  99. }
  100. OC_App::registerAutoloading($appId, $path);
  101. /*
  102. * Next we check if there is an application class and it implements
  103. * the \OCP\AppFramework\Bootstrap\IBootstrap interface
  104. */
  105. $appNameSpace = App::buildAppNamespace($appId);
  106. $applicationClassName = $appNameSpace . '\\AppInfo\\Application';
  107. try {
  108. if (class_exists($applicationClassName) && in_array(IBootstrap::class, class_implements($applicationClassName), true)) {
  109. try {
  110. /** @var IBootstrap|App $application */
  111. $apps[$appId] = $application = $this->serverContainer->query($applicationClassName);
  112. } catch (QueryException $e) {
  113. // Weird, but ok
  114. continue;
  115. }
  116. $this->eventLogger->start('bootstrap:register_app_' . $appId, '');
  117. $application->register($this->registrationContext->for($appId));
  118. $this->eventLogger->end('bootstrap:register_app_' . $appId);
  119. }
  120. } catch (Throwable $e) {
  121. $this->logger->emergency('Error during app service registration: ' . $e->getMessage(), [
  122. 'exception' => $e,
  123. 'app' => $appId,
  124. ]);
  125. continue;
  126. }
  127. }
  128. /**
  129. * Now that all register methods have been called, we can delegate the registrations
  130. * to the actual services
  131. */
  132. $this->registrationContext->delegateCapabilityRegistrations($apps);
  133. $this->registrationContext->delegateCrashReporterRegistrations($apps, $this->registry);
  134. $this->registrationContext->delegateDashboardPanelRegistrations($this->dashboardManager);
  135. $this->registrationContext->delegateEventListenerRegistrations($this->eventDispatcher);
  136. $this->registrationContext->delegateContainerRegistrations($apps);
  137. $this->registrationContext->delegateMiddlewareRegistrations($apps);
  138. }
  139. public function getRegistrationContext(): ?RegistrationContext {
  140. return $this->registrationContext;
  141. }
  142. public function bootApp(string $appId): void {
  143. if (isset($this->bootedApps[$appId])) {
  144. return;
  145. }
  146. $this->bootedApps[$appId] = true;
  147. $appNameSpace = App::buildAppNamespace($appId);
  148. $applicationClassName = $appNameSpace . '\\AppInfo\\Application';
  149. if (!class_exists($applicationClassName)) {
  150. // Nothing to boot
  151. return;
  152. }
  153. /*
  154. * Now it is time to fetch an instance of the App class. For classes
  155. * that implement \OCP\AppFramework\Bootstrap\IBootstrap this means
  156. * the instance was already created for register, but any other
  157. * (legacy) code will now do their magic via the constructor.
  158. */
  159. $this->eventLogger->start('bootstrap:boot_app_' . $appId, '');
  160. try {
  161. /** @var App $application */
  162. $application = $this->serverContainer->query($applicationClassName);
  163. if ($application instanceof IBootstrap) {
  164. /** @var BootContext $context */
  165. $context = new BootContext($application->getContainer());
  166. $application->boot($context);
  167. }
  168. } catch (QueryException $e) {
  169. $this->logger->error("Could not boot $appId: " . $e->getMessage(), [
  170. 'exception' => $e,
  171. ]);
  172. } catch (Throwable $e) {
  173. $this->logger->emergency("Could not boot $appId: " . $e->getMessage(), [
  174. 'exception' => $e,
  175. ]);
  176. }
  177. $this->eventLogger->end('bootstrap:boot_app_' . $appId);
  178. }
  179. public function isBootable(string $appId) {
  180. $appNameSpace = App::buildAppNamespace($appId);
  181. $applicationClassName = $appNameSpace . '\\AppInfo\\Application';
  182. return class_exists($applicationClassName) &&
  183. in_array(IBootstrap::class, class_implements($applicationClassName), true);
  184. }
  185. }