Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for re-generating endpoints without restart #1146

Merged
merged 22 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c3117a2
feat: add support for re-generating endpoints without restart
taefi Jun 30, 2023
f530ad9
add browser refresh after registering endpoint changes
taefi Jul 21, 2023
c570d1b
fix autoconfiguration for hot swap config
taefi Jul 21, 2023
770cee6
Merge branch 'main' into taefi/hotswap-without-restart
taefi Jul 24, 2023
6513dd7
add configurable property to disable/enable the endpoint hot-reload
taefi Jul 27, 2023
5424bbb
Endpoint polling
cromoteca Aug 8, 2023
c9d305b
replacing watch service with polling to detect endpoint changes
taefi Aug 16, 2023
03c48e9
rollback unnecessary changes
taefi Aug 16, 2023
ca2d445
Merge branch 'main' into feat/endpoint-polling
taefi Aug 16, 2023
961dca3
rename method to a more meaningful name
taefi Aug 16, 2023
2c39b02
add info level log about how to configure the polling interval
taefi Aug 16, 2023
4c117d6
add tests
taefi Aug 16, 2023
4758e97
Merge branch 'main' into feat/endpoint-polling
taefi Aug 17, 2023
b2d0b4f
fix tests
taefi Aug 17, 2023
fb02599
formatter:format
taefi Aug 17, 2023
6327998
Merge branch 'main' into feat/endpoint-polling
taefi Aug 17, 2023
0a52eb9
use @ConfigurationProperties instead of @Value to enable autocomplete
taefi Aug 17, 2023
30bd4d3
formatter:format
taefi Aug 17, 2023
a2928ec
Merge branch 'main' into feat/endpoint-polling
taefi Aug 17, 2023
139808c
Merge branch 'main' into feat/endpoint-polling
platosha Aug 22, 2023
ba94f34
Merge branch 'main' into feat/endpoint-polling
Artur- Aug 24, 2023
394c53a
Merge branch 'main' into feat/endpoint-polling
taefi Aug 24, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/java/endpoint/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@
<artifactId>spring-boot-autoconfigure</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2022 Vaadin Ltd.
* Copyright 2000-2023 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright 2000-2023 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/

package dev.hilla.internal.hotswap;

import com.vaadin.flow.internal.BrowserLiveReload;
import dev.hilla.EndpointController;
import dev.hilla.engine.EngineConfiguration;
import dev.hilla.engine.GeneratorProcessor;
import dev.hilla.engine.ParserProcessor;
import jakarta.annotation.PostConstruct;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.file.Path;
import java.util.Optional;

class EndpointHotSwapListener implements HotSwapListener {

private static final Logger LOGGER = LoggerFactory
.getLogger(EndpointHotSwapListener.class);

private final EndpointController endpointController;

private final EndpointHotSwapService endpointHotSwapService;

public EndpointHotSwapListener(EndpointController endpointController,
EndpointHotSwapService endpointHotSwapService) {
this.endpointController = endpointController;
this.endpointHotSwapService = endpointHotSwapService;
}

@PostConstruct
private void registerForHotSwapChanges() {
endpointHotSwapService.addHotSwapListener(this);
}

@Override
public void endpointChanged(EndpointChangedEvent event) {
EngineConfiguration engineConfiguration;
try {
engineConfiguration = EngineConfiguration
.loadDirectory(event.buildDir());
} catch (IOException e) {
throw new RuntimeException(e);
}
ParserProcessor parser = new ParserProcessor(engineConfiguration,
this.getClass().getClassLoader());
parser.process();
GeneratorProcessor generator = new GeneratorProcessor(
engineConfiguration, "node");
generator.process();

this.endpointController.registerEndpoints();
reload(event.browserLiveReload());
Artur- marked this conversation as resolved.
Show resolved Hide resolved
}

private void reload(BrowserLiveReload browserLiveReload) {

Optional.ofNullable(browserLiveReload).ifPresent(liveReload -> {
LOGGER.debug("Reloading the browser after endpoint(s) changes...");
liveReload.reload();
});
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright 2000-2023 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/

package dev.hilla.internal.hotswap;

import com.vaadin.flow.internal.BrowserLiveReload;
import com.vaadin.flow.shared.Registration;

import java.nio.file.Path;

/**
* The service interface that defines the API for EndpointHotSwapService
* implementations.
*/
public interface EndpointHotSwapService {

/**
* The method that starts the process for monitoring the changes in
* endpoints. The strategy for detecting the changes can be Polling,
* Watching the File System changes, etc. No matter what the strategy for
* detecting the changes is, instances of {@code EndpointChangedEvent}
* should be fired upon detecting changes in the endpoints and associated
* classes.
*
* @param buildDir
* the path to project's build directory which is 'target' in
* standard Maven projects and 'build' in standard Gradle
* projects.
* @param browserLiveReload
* the BrowserLiveReload object which is used to reload the
* browser after any endpoint changes are detected.
*
* @see EndpointHotSwapService#addHotSwapListener(HotSwapListener)
*/
void monitorChanges(Path buildDir, BrowserLiveReload browserLiveReload);

/**
* The method that enables registering {@code HotSwapListener}s to be
* notified whenever an {@code EndpointChangedEvent} is fired.
*
* @param listener
* an instance of HotSwapListener that implements the
* {@code endpointChanged} method that will be called when an
* {@code EndpointChangedEvent} is fired.
* @return the Listener Registration object that enables the cally to
* deregister the listener.
*/
Registration addHotSwapListener(HotSwapListener listener);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright 2000-2023 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/

package dev.hilla.internal.hotswap;

import dev.hilla.EndpointController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class HotSwapConfiguration {

@Bean
@ConfigurationProperties(prefix = "hilla.endpoint.hot-reload")
public HotSwapConfigurationProperties hotSwapConfigurationProperties() {
return new HotSwapConfigurationProperties();
}

@Bean
EndpointHotSwapService hotSwapWatchService(
@Autowired HotSwapConfigurationProperties configurationProperties) {
return new PollChangedEndpointsHotSwapService(
configurationProperties.getPollInterval());
}

@Bean
HotSwapServiceInitializer hotSwapServiceInitializer(
@Autowired EndpointHotSwapService endpointHotSwapService,
@Autowired HotSwapConfigurationProperties configurationProperties) {
return new HotSwapServiceInitializer(endpointHotSwapService,
configurationProperties.isEnabled());
}

@Bean
EndpointHotSwapListener endpointHotSwapListener(
@Autowired EndpointHotSwapService endpointHotSwapService,
@Autowired EndpointController endpointController) {
return new EndpointHotSwapListener(endpointController,
endpointHotSwapService);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package dev.hilla.internal.hotswap;

public class HotSwapConfigurationProperties {
/**
* Whether the endpoint changes hot-reload is enabled or not.
*/
private boolean enabled = true;

/**
* The interval to poll for endpoint changes in seconds.
*/
private int pollInterval = 5;

public boolean isEnabled() {
return enabled;
}

public void setEnabled(boolean enabled) {
this.enabled = enabled;
}

public int getPollInterval() {
return pollInterval;
}

public void setPollInterval(int pollInterval) {
this.pollInterval = pollInterval;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2000-2023 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/

package dev.hilla.internal.hotswap;

import com.vaadin.flow.internal.BrowserLiveReload;

import java.nio.file.Path;

/**
* The interface that defines the contract for HotSwapListeners.
*/
public interface HotSwapListener {

/**
* The event object that is passed to {@code endpointChanged} method.
* @param buildDir
* @param browserLiveReload
*/
record EndpointChangedEvent(Path buildDir, BrowserLiveReload browserLiveReload) {
}

/**
* The method that is called when an {@code EndpointChangedEvent} is fired.
*/
void endpointChanged(EndpointChangedEvent event);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright 2000-2023 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/

package dev.hilla.internal.hotswap;

import com.vaadin.flow.internal.BrowserLiveReload;
import com.vaadin.flow.internal.BrowserLiveReloadAccessor;
import com.vaadin.flow.server.ServiceInitEvent;
import com.vaadin.flow.server.VaadinService;
import com.vaadin.flow.server.VaadinServiceInitListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.file.Path;

class HotSwapServiceInitializer implements VaadinServiceInitListener {

private final Logger LOGGER;

boolean endpointHotReloadEnabled;

private final EndpointHotSwapService endpointHotSwapService;

public HotSwapServiceInitializer(
EndpointHotSwapService endpointHotSwapService,
boolean endpointHotReloadEnabled) {
this.endpointHotSwapService = endpointHotSwapService;
this.endpointHotReloadEnabled = endpointHotReloadEnabled;
LOGGER = LoggerFactory.getLogger(this.getClass());
}

@Override
public void serviceInit(ServiceInitEvent serviceInitEvent) {
VaadinService vaadinService = serviceInitEvent.getSource();
BrowserLiveReloadAccessor.getLiveReloadFromService(vaadinService)
.ifPresent(browserLiveReload -> {
if (BrowserLiveReload.Backend.SPRING_BOOT_DEVTOOLS != browserLiveReload
.getBackend()
&& isDevModeLiveReloadEnabled(vaadinService)) {
if (isEndpointHotReloadEnabled()) {
endpointHotSwapService.monitorChanges(
getBuildDir(vaadinService),
browserLiveReload);
info("Hilla Endpoint Hot-Reload service is enabled. "
+ "You can disable it by defining the hilla.endpoint.hot-reload.enabled=false property in application.properties file.");
info("The default polling interval for Hilla Endpoint Hot-Reload is 5 seconds. "
+ "You can change the interval by defining a positive value for the hilla.endpoint.hot-reload.pollInterval property in application.properties file.");
} else {
info("Hilla Endpoint Hot-Reload service is disabled. "
+ "You can enable it by removing the hilla.endpoint.hot-reload.enabled=true property from your application.properties file, "
+ "or by setting its value to true.");
}
}
});
}

private boolean isDevModeLiveReloadEnabled(VaadinService vaadinService) {
return vaadinService.getDeploymentConfiguration()
.isDevModeLiveReloadEnabled();
}

private Path getBuildDir(VaadinService vaadinService) {
var deploymentConfig = vaadinService.getDeploymentConfiguration();
var projectFolder = deploymentConfig.getProjectFolder().toPath();
return projectFolder.resolve(deploymentConfig.getBuildFolder());
}

private boolean isEndpointHotReloadEnabled() {
return endpointHotReloadEnabled;
}

private void info(String message) {
LOGGER.info(message);
}
}
Loading