HotswapProjects / HotswapAgent

Java unlimited redefinition of classes at runtime.
GNU General Public License v2.0
2.36k stars 493 forks source link

Resources are no longer watched after modifying through IntelliJ #596

Open Artur- opened 1 month ago

Artur- commented 1 month ago

If I have a resource, say src/main/resources/vaadin-i18n/translations.properties containing foo=bar, then when running with HotswapAgent and doing

touch target/classes/vaadin-i18n/translations.properties

triggers hotswap events for file reload, triggers the Vaadin plugin etc. Just like expected.

However, if you in IntelliJ open the file, change it to foo=baz and press Build -> Recompile 'translations.properties' then the following is logged

HOTSWAP AGENT: 11:25:14.802 DEBUG (org.hotswap.agent.watch.nio.WatcherNIO2) - Watcher on /.../target/classes/vaadin-i18n not valid, removing path=
HOTSWAP AGENT: 11:25:14.947 DEBUG (org.hotswap.agent.watch.nio.WatcherNIO2) - Watch event 'ENTRY_MODIFY' on '/.../target/classes/vaadin-i18n' --> vaadin-i18n

and there is no hotswap event received.

After that, no hotswap events are sent any more when updating from outside IntelliJ either, i.e. HotswapAgent no longer reacts to

touch target/classes/vaadin-i18n/translations.properties

in any way

Artur- commented 1 month ago

To make it more interesting, if the modified resource is directly in src/main/resources then there is no logging about the watcher not being valid, it is not removed, modifying from inside IntelliJ triggers reload events - it all works fine

skybber commented 1 month ago

try to add following line to hotswap-agent.properties in your application resources/

LOGGER.org.hotswap.agent.watch=trace

you should see sequence of WATCH EVENTS generated by Idea deployment and compare it to touch command, pby there is a problem in event handler (Watcher) in HA.

Artur- commented 1 month ago

Inside IntelliJ it is the above mentioned rows

HOTSWAP AGENT: 11:25:14.802 DEBUG (org.hotswap.agent.watch.nio.WatcherNIO2) - Watcher on /.../target/classes/vaadin-i18n not valid, removing path=
HOTSWAP AGENT: 11:25:14.947 DEBUG (org.hotswap.agent.watch.nio.WatcherNIO2) - Watch event 'ENTRY_MODIFY' on '/.../target/classes/vaadin-i18n' --> vaadin-i18n

and outside (touch) it is only

HOTSWAP AGENT: 12:23:03.040 DEBUG (org.hotswap.agent.watch.nio.WatcherNIO2) - Watch event 'ENTRY_MODIFY' on '/.../target/classes/vaadin-i18n/translations.properties' --> translations.properties

I think IntelliJ does something like

rm target/classes/vaadin-i18n/translations.properties && rmdir target/classes/vaadin-i18n && mkdir target/classes/vaadin-i18n && cp translations.properties target/classes/vaadin-i18n/translations.properties

when updating the files, which causes similar logs as with IntelliJ

skybber commented 1 month ago

Is the "Hotswap Watcher" thread still running after it stops responding?

Artur- commented 1 month ago

The watcher thread keeps running and only the listener for the vaadin-i18n/translations.properties stops working. Doing touch target/classes/foo/bar.properties still triggers an event.

Also, if you do the command slowly, step by step, the output is quite different: rm target/classes/vaadin-i18n/translations.properties

HOTSWAP AGENT: 14:30:43.072 DEBUG (org.hotswap.agent.watch.nio.WatcherNIO2) - Watch event 'ENTRY_DELETE' on '/.../target/classes/vaadin-i18n/translations.properties' --> translations.properties

rmdir target/classes/vaadin-i18n

HOTSWAP AGENT: 14:31:15.271 DEBUG (org.hotswap.agent.watch.nio.WatcherNIO2) - Watcher on /.../target/classes/vaadin-i18n not valid, removing path=
HOTSWAP AGENT: 14:31:15.524 DEBUG (org.hotswap.agent.watch.nio.WatcherNIO2) - Watch event 'ENTRY_DELETE' on '/.../target/classes/vaadin-i18n' --> vaadin-i18n

mkdir target/classes/vaadin-i18n

HOTSWAP AGENT: 14:32:04.229 DEBUG (org.hotswap.agent.watch.nio.WatcherNIO2) - Watch event 'ENTRY_CREATE' on '/.../target/classes/vaadin-i18n' --> vaadin-i18n
HOTSWAP AGENT: 14:32:04.230 DEBUG (org.hotswap.agent.watch.nio.WatcherNIO2) - Registering directory  /.../target/classes/vaadin-i18n

cp translations.properties target/classes/vaadin-i18n/translations.properties

HOTSWAP AGENT: 14:32:52.979 DEBUG (org.hotswap.agent.watch.nio.WatcherNIO2) - Watch event 'ENTRY_CREATE' on '/.../target/classes/vaadin-i18n/translations.properties' --> translations.properties
skybber commented 1 month ago

What OS are you using?

Artur- commented 1 month ago

macOS 15.0 aka sequoia

skybber commented 1 month ago

There is WatcherNIO2Test in HA core, I've tried to simulate your problem, and don't see some events on Linux, too.

    @Test
    public void recreateDirFile() throws IOException {
        final ResultHolder resultHolder = new ResultHolder();

        Path testDir = temp.resolve("testdir");
        Files.createDirectories(testDir);
        File testFile = new File(testDir.toFile(), "testfile.txt");
        testFile.createNewFile();

        watcher.addEventListener(null, temp.toUri(), new WatchEventListener() {
            @Override
            public void onEvent(WatchFileEvent event) {
                System.out.println(event.toString());
                // assertEquals("New file event type", FileEvent.CREATE, event.getEventType());
                // assertTrue("File name", event.getURI().toString().endsWith("testfile.txt"));
                resultHolder.result = true;
            }
        });

        //assertTrue("Event listener called", waitForResult(resultHolder));

        Files.walk(testDir)
            .sorted(Comparator.reverseOrder())
            .forEach(path -> {
                try {
                    Files.delete(path);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
        Files.deleteIfExists(testDir);
        Files.createDirectories(testDir);
        testFile = new File(testDir.toFile(), "testfile.txt");
        testFile.createNewFile();

        assertTrue("Event listener called", waitForResult(resultHolder));
    }
skybber commented 1 month ago

Problem is in WatcherNIO2.registerAll, it is called too late, after directory+files are recreated. I've tried to "touch" existing files inside preVisitDirectory and it generates modify event, but additional touch could have side effects. So only solution I see is to make "fake" create events and feed dispatcher.add(ev, child); with them.

skybber commented 1 month ago

There is a fix for it in the master + junit test. Could you check it please?

Artur- commented 1 month ago

Based on a quick test, it works perfectly. The output is

HOTSWAP AGENT: 19:38:28.283 DEBUG (org.hotswap.agent.watch.nio.WatcherNIO2) - Watch event 'ENTRY_MODIFY' on '/.../target/classes/vaadin-i18n/translations.properties' --> translations.properties

and nothing else

skybber commented 1 month ago

Added LOG for fake events, you should see ENTRY_CREATE event

skybber commented 1 month ago

Otherwise, this change breaks the Spring plugin tests, the other plugins remain unaffected.

skybber commented 1 month ago

Tests are fixed now.

skybber commented 1 month ago

There may be a similar problem on Windows, as it uses a different implementation of Watcher. Have you had the opportunity to test your issue on Windows?

Artur- commented 1 month ago

I tried in a virtual machine with Windows 11 and don't see the original issue with HA 2.0.1. It logs

HOTSWAP AGENT: 10:25:05.686 DEBUG (org.hotswap.agent.watch.nio.TreeWatcherNIO) - Watch event 'ENTRY_DELETE' on '...\target\classes\i18n\translations.properties' --> i18n\translations.properties
HOTSWAP AGENT: 10:25:05.719 DEBUG (org.hotswap.agent.watch.nio.TreeWatcherNIO) - Watch event 'ENTRY_MODIFY' on '...\target\classes\i18n' --> i18n
HOTSWAP AGENT: 10:25:05.720 DEBUG (org.hotswap.agent.watch.nio.TreeWatcherNIO) - Watch event 'ENTRY_DELETE' on '...\target\classes\i18n' --> i18n
HOTSWAP AGENT: 10:25:05.748 DEBUG (org.hotswap.agent.watch.nio.TreeWatcherNIO) - Watch event 'ENTRY_CREATE' on '...\target\classes\i18n' --> i18n
HOTSWAP AGENT: 10:25:05.748 INFO (org.hotswap.agent.watch.nio.TreeWatcherNIO) - Registering directory ...\target\classes\i18n 
HOTSWAP AGENT: 10:25:05.748 DEBUG (org.hotswap.agent.watch.nio.TreeWatcherNIO) - Path ...\target\classes\i18n watched via ...\target\classes
HOTSWAP AGENT: 10:25:05.749 DEBUG (org.hotswap.agent.watch.nio.TreeWatcherNIO) - Watch event 'ENTRY_CREATE' on '...\target\classes\i18n\translations.properties' --> i18n\translations.properties
HOTSWAP AGENT: 10:25:05.750 DEBUG (org.hotswap.agent.watch.nio.TreeWatcherNIO) - Watch event 'ENTRY_MODIFY' on '...\target\classes\i18n\translations.properties' --> i18n\translations.properties
HOTSWAP AGENT: 10:25:05.751 DEBUG (org.hotswap.agent.watch.nio.TreeWatcherNIO) - Watch event 'ENTRY_MODIFY' on '...\target\classes\i18n\translations.properties' --> i18n\translations.properties
HOTSWAP AGENT: 10:25:05.766 DEBUG (org.hotswap.agent.watch.nio.TreeWatcherNIO) - Watch event 'ENTRY_MODIFY' on '...\target\classes\i18n' --> i18n

and the watcher keeps working

Artur- commented 1 month ago

Now when testing some more on Mac, there are still some issues. Either I am testing it wrong or something is still bugging.

With the latest master, when changing vaadin-i18n/translations.properties in IntelliJ, I see

HOTSWAP AGENT: 11:03:21.338 DEBUG (org.hotswap.agent.watch.nio.WatcherNIO2) - Watcher on /.../target/classes/vaadin-i18n not valid, removing path=
HOTSWAP AGENT: 11:03:21.547 DEBUG (org.hotswap.agent.watch.nio.WatcherNIO2) - Watch event 'ENTRY_MODIFY' on '/.../target/classes/vaadin-i18n' --> vaadin-i18n

and then on subsequent changes only

HOTSWAP AGENT: 11:03:21.547 DEBUG (org.hotswap.agent.watch.nio.WatcherNIO2) - Watch event 'ENTRY_MODIFY' on '/.../target/classes/vaadin-i18n' --> vaadin-i18n

there are no modify events for the file itself and manually doing touch target/classes/vaadin-i18n/translations.properties does nog log anything from org.hotswap.agent.watch

Artur- commented 1 month ago

I don't get it. Now it works like yesterday again... I wonder if there is a timing issue still somewhere

skybber commented 1 month ago

Could you make simple example for testing?