App Development: Getting Exception on Event

Hello!

I am trying to write an app that listens to Events and sends a notification to a backend.
I am using the official Docker image and i copy my code into the custom_apps directory via docker cp.

Here is what i think are the important parts of my code

<?php

namespace OCA\IntrafindConnector\AppInfo;

use OCA\IntrafindConnector\Event\NodeCreatedListener;
use OCA\IntrafindConnector\Event\UserCreatedListener;
use OCP\AppFramework\App;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Events\Node\NodeCreatedEvent;
use OCP\User\Events\UserCreatedEvent;

class Application extends App {
	public const APP_ID = 'intrafindconnector';

	public function __construct() {
		parent::__construct(self::APP_ID);

		/* @var IEventDispatcher $eventDispatcher */
		$dispatcher = $this->getContainer()->get(IEventDispatcher::class);
		$dispatcher->addServiceListener(NodeCreatedEvent::class, NodeCreatedListener::class);
		$dispatcher->addServiceListener(UserCreatedEvent::class, UserCreatedListener::class);
	}
}
<?php


namespace OCA\IntrafindConnector\Event;

use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use Psr\Log\LoggerInterface;

class UserCreatedListener implements IEventListener {
	private $logger;
	private $appName;

	public function __construct(LoggerInterface $logger, string $appName) {
		$this->logger = $logger;
		$this->appName = $appName;
	}

	/**
	 * @inheritDoc
	 */
	public function handle(Event $event): void {
		$user = $event->getUser();
		$this->logger->warning($user->getDisplayName());
	}
}

The idea is that i create a new user and the username gets written into the log file (of course only for testing purposes). Unfortunately i get the following exception

[no app in context] Error: OCP\AppFramework\QueryException: Could not resolve OCA\IntrafindConnector\Event\UserCreatedListener! Class OCA\IntrafindConnector\Event\UserCreatedListener does not exist at <<closure>>

 0. /var/www/html/lib/private/AppFramework/Utility/SimpleContainer.php line 126
    OC\AppFramework\Utility\SimpleContainer->resolve("OCA\\IntrafindC ... r")
 1. /var/www/html/lib/private/ServerContainer.php line 162
    OC\AppFramework\Utility\SimpleContainer->query("OCA\\IntrafindC ... r", true)
 2. /var/www/html/lib/private/EventDispatcher/ServiceEventListener.php line 67
    OC\ServerContainer->query("OCA\\IntrafindC ... r")
 3. /var/www/html/3rdparty/symfony/event-dispatcher/EventDispatcher.php line 251
    OC\EventDispatcher\ServiceEventListener->__invoke(OCP\User\Events\UserCreatedEvent {}, "OCP\\User\\Events\\UserCreatedEvent", Symfony\Componen ... {})
 4. /var/www/html/3rdparty/symfony/event-dispatcher/EventDispatcher.php line 73
    Symfony\Component\EventDispatcher\EventDispatcher->callListeners([Closure {}], "OCP\\User\\Events\\UserCreatedEvent", OCP\User\Events\UserCreatedEvent {})
 5. /var/www/html/lib/private/EventDispatcher/EventDispatcher.php line 86
    Symfony\Component\EventDispatcher\EventDispatcher->dispatch(OCP\User\Events\UserCreatedEvent {}, "OCP\\User\\Events\\UserCreatedEvent")
 6. /var/www/html/lib/private/EventDispatcher/EventDispatcher.php line 98
    OC\EventDispatcher\EventDispatcher->dispatch("OCP\\User\\Events\\UserCreatedEvent", OCP\User\Events\UserCreatedEvent {})
 7. /var/www/html/lib/private/User/Manager.php line 460
    OC\EventDispatcher\EventDispatcher->dispatchTyped(OCP\User\Events\UserCreatedEvent {})
 8. /var/www/html/lib/private/User/Manager.php line 396
    OC\User\Manager->createUserFromBackend("*** sensitive parameter replaced ***", "*** sensitive parameter replaced ***", OC\User\Database {})
 9. /var/www/html/apps/provisioning_api/lib/Controller/UsersController.php line 412
    OC\User\Manager->createUser("*** sensitive parameter replaced ***", "*** sensitive parameter replaced ***")
10. /var/www/html/lib/private/AppFramework/Http/Dispatcher.php line 218
    OCA\Provisioning_API\Controller\UsersController->addUser("*** sensitive parameters replaced ***")
11. /var/www/html/lib/private/AppFramework/Http/Dispatcher.php line 127
    OC\AppFramework\Http\Dispatcher->executeController(OCA\Provisioning ... {}, "addUser")
12. /var/www/html/lib/private/AppFramework/App.php line 157
    OC\AppFramework\Http\Dispatcher->dispatch(OCA\Provisioning ... {}, "addUser")
13. /var/www/html/lib/private/Route/Router.php line 302
    OC\AppFramework\App::main("OCA\\Provisioni ... r", "addUser", OC\AppFramework\ ... {}, {_route: "ocs.pr ... "})
14. /var/www/html/ocs/v1.php line 63
    OC\Route\Router->match("/ocsapp/cloud/users")
15. /var/www/html/ocs/v2.php line 24
    require_once("/var/www/html/ocs/v1.php")

So from what i can tell the event gets caught but then my Listener class doesn’t get resolved.

What do i need to do that my Listener is resolved? Did i miss something?

I want to do basically the same with files. When a new file is uploaded, i want to write the filename to the logfile. But in this case, the event isn’t even caught.
In Owncloud, if you want to use filesystem hooks, you have to add

    <types>
        <filesystem/>
    </types>

to the info.xml.
Do i have to do the same or a similiar thing here as well to use the filesystem events?

Thank you and regards

Fabian

The code looks fine.

I would test two things

  1. Try without the injected $appName. I’m not sure if we use the app container or the server container to resolve the listener. Only the appropriate app container will know how to resolve this parameter.
  2. Make sure the file is named correctly and placed in the directory that will be looked at for autoloading. https://docs.nextcloud.com/server/stable/developer_manual/digging_deeper/classloader.html?highlight=namespace#psr-4-autoloading

PS: don’t use the constructor to register services of your app. Use the provided lifecycle hooks: https://docs.nextcloud.com/server/latest/developer_manual/app_development/bootstrap.html#bootstrapping-process

1 Like

Unfortunately this is a bit of a bug in the current API: Clarify that lazy event listeners are built from the server container by ChristophWurst · Pull Request #27794 · nextcloud/server · GitHub :wink:

Hello Christoph!

Thanks for the help!

So i used IBootstrap instead of constructor and removed the $app parameter.

Now the event is caught and the username is written to the logs. So one part already works.

Unfortunately the filesystem event is not triggered.

I upload a document into the “Dateien” section and it looks like that the event is not caught. I don’t even see any error in the logs, just nothing happens.

So here is the new code of Application.php

<?php

namespace OCA\IntrafindConnector\AppInfo;

use OCA\IntrafindConnector\EventListener\NodeCreatedListener;
use OCA\IntrafindConnector\EventListener\UserCreatedListener;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCP\Files\Events\Node\NodeCreatedEvent;
use OCP\User\Events\UserCreatedEvent;

class Application extends App implements IBootstrap {
	public const APP_ID = 'intrafindconnector';

	public function __construct() {
		parent::__construct(self::APP_ID);
	}

	public function register(IRegistrationContext $context): void {
		// TODO: Implement register() method.
		// Register the composer autoloader for packages shipped by this app, if applicable
		include_once __DIR__ . '/../../vendor/autoload.php';

		$context->registerEventListener(NodeCreatedEvent::class, NodeCreatedListener::class);
		$context->registerEventListener(UserCreatedEvent::class, UserCreatedListener::class);
	}

	public function boot(IBootContext $context): void {
		// TODO: Implement boot() method.
	}
}

and here is the code of the event listener

<?php


namespace OCA\IntrafindConnector\EventListener;

use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use Psr\Log\LoggerInterface;

class NodeCreatedListener implements IEventListener {
	private $logger;

	public function __construct(LoggerInterface $logger) {
		$this->logger = $logger;
	}

	/**
	 * @inheritDoc
	 */
	public function handle(Event $event): void {
		$node = $event->getNode();
		$this->logger->error("Filename:" . $node->getName());
	}
}

Is the NodeCreatedEvent the correct event that i try to catch? Or is there another event that i have to catch?
Or do i have to to something additional to make the file event work?

Regards

Fabian

The event is not documented at https://docs.nextcloud.com/server/stable/developer_manual/basics/events.html, which is a bit suspicious.

That event should be emitted from \OC\Files\Node\HookConnector::postCreate. There is code responsible for invoking this method from \OC\Files\Node\HookConnector::viewToNode. If you have a debugger I would highly appreciate if you could debug this yourself as I lack the time right now. Is that something you would be comfortable with?

1 Like

That was what i found as well, so i thought i could use these events.

Funny enough, now the event works. All i did was restarting the Docker container and deactivate and activate the app.

I also tested the NodeDeletedEvent event, which also worked.

Not sure if the container restart or deactivating the app did the trick, but eventually it works for me :smiley:.

Regards

Fabian

Well, let’s say good to hear it works now.

There is no obvious link between restarting the server and triggering of the event. Though possibly some aggressive opcache in php could lead to some old code getting executed. Otherwise there isn’t much that Nextcloud would cache in terms of events.