Reputation: 1163
UPDATE ON THE SOLUTION This bug seems to have been the cause of the problem: https://github.com/vaadin/flow/issues/6657
While the fix is rolled out the marked answer can be used as a workaround
Background
I'm in the process of creating a dockerized, Spring Boot + Vaadin flow based portal for my IoT project.
Problem
The portal works well running from my machine in Vaadin's development mode, with npm installed, but I can't seem to get the right setup for creating a lightweight deployment jar that already contains all the generated front end components.
When I'm running the service in a Docker container, I get the error that it cannot find npm but I assume that it shouldn't be needed for a production deployment
at com.vaadin.flow.server.startup.DevModeInitializer.initDevModeHandler(DevModeInitializer.java:327)
at com.vaadin.flow.spring.VaadinServletContextInitializer$DevModeServletContextListener.contextInitialized(VaadinServletContextInitializer.java:323)
... 46 common frames omitted
Caused by: com.vaadin.flow.server.ExecutionFailedException:
Failed to determine 'npm' tool.
Please install it either:
- by following the https://nodejs.org/en/download/ guide to install it globally
- or by running the frontend-maven-plugin goal to install it in this project:
$ mvn com.github.eirslett:frontend-maven-plugin:1.7.6:install-node-and-npm -DnodeVersion="v12.13.0"
Docker file without npm:
FROM openjdk:11-jre-slim
ARG PROJECT
ARG SERVICE_PORT
ARG JAR_FILE
EXPOSE ${SERVICE_PORT}
RUN mkdir /${PROJECT}
WORKDIR /${PROJECT}
ADD target/${JAR_FILE} ./app.jar
CMD ["java","-jar","app.jar"]
pom.xml (updated!)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.tlvlp</groupId>
<artifactId>iot-portal</artifactId>
<version>0.0.2</version>
<name>iot-portal</name>
<description>tlvlp IoT server portal</description>
<properties>
<java.version>11</java.version>
<vaadin.version>14.0.12</vaadin.version>
<dockerfile.maven.version>1.4.13</dockerfile.maven.version>
<flow.server.prod.version>2.0.17</flow.server.prod.version>
<!-- DOCKER IMAGE ARGS -->
<docker.project.repository>tlvlp/iot-portal</docker.project.repository>
<service.port>8600</service.port>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-bom</artifactId>
<version>${vaadin.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>prod</id>
<properties>
<activatedProperties>prod</activatedProperties>
<vaadin.productionMode>true</vaadin.productionMode>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<dependencies>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>flow-server-production-mode</artifactId>
<version>${flow.server.prod.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.vaadin</groupId>
<artifactId>flow-maven-plugin</artifactId>
<version>${flow.server.prod.version}</version>
<executions>
<execution>
<goals>
<goal>build-frontend</goal>
<goal>copy-production-files</goal>
<goal>package-for-production</goal>
</goals>
<phase>compile</phase>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>${dockerfile.maven.version}</version>
<executions>
<execution>
<id>prod</id>
<goals>
<goal>build</goal>
<goal>push</goal>
</goals>
</execution>
</executions>
<configuration>
<repository>${docker.project.repository}</repository>
<tag>${project.version}</tag>
<tag>latest</tag>
<buildArgs>
<PROJECT>${project.groupId}.${project.artifactId}</PROJECT>
<SERVICE_PORT>${service.port}</SERVICE_PORT>
<JAR_FILE>${project.build.finalName}.jar</JAR_FILE>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>dev</id>
<properties>
<activatedProperties>dev</activatedProperties>
<vaadin.productionMode>false</vaadin.productionMode>
</properties>
</profile>
</profiles>
</project>
application.properties
spring.profiles.active=@activatedProperties@
application-prod.properties
vaadin.compatibilityMode=false
vaadin.servlet.productionMode=true
I appreciate any guidance :)
All the code can be found on the master branch of the project's public repository: https://github.com/tlvlp/iot-portal
Upvotes: 1
Views: 2319
Reputation: 1163
I've finally managed to figure out the solution to the problem. It was a correct assumption that npm is not required to run the production application. What wasn't clear from even the official guide that although adding the following property to the prod build profile does populate to the built jar file, but
in itself does NOT trigger Vaadin to run in production mode (at least not together with SpringBoot):
<vaadin.productionMode>true</vaadin.productionMode>
Solution:
What ended up solving the problem is to assign the value of the same parameter in the SpringBoot properties file as well. Then it started to use the assets generated using npm in build time.
Here is the application.properties file (shared by both dev and prod properties files).
[email protected]@
[email protected]@
vaadin.compatibilityMode=false
Here is the complete and updated and working pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.tlvlp</groupId>
<artifactId>iot-portal</artifactId>
<version>0.0.2</version>
<name>iot-portal</name>
<description>tlvlp IoT server portal</description>
<properties>
<java.version>11</java.version>
<vaadin.version>14.0.12</vaadin.version>
<dockerfile.maven.version>1.4.13</dockerfile.maven.version>
<flow.server.prod.version>2.0.17</flow.server.prod.version>
<!-- DOCKER IMAGE ARGS -->
<docker.project.repository>tlvlp/iot-portal</docker.project.repository>
<service.port>8600</service.port>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-bom</artifactId>
<version>${vaadin.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>prod</id>
<properties>
<spring.activatedProperties>prod</spring.activatedProperties>
<vaadin.productionMode>true</vaadin.productionMode>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<dependencies>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>flow-server-production-mode</artifactId>
<version>${flow.server.prod.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.vaadin</groupId>
<artifactId>flow-maven-plugin</artifactId>
<version>${flow.server.prod.version}</version>
<executions>
<execution>
<goals>
<goal>prepare-frontend</goal>
<goal>build-frontend</goal>
</goals>
<phase>compile</phase>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>${dockerfile.maven.version}</version>
<executions>
<execution>
<id>prod</id>
<goals>
<goal>build</goal>
<goal>push</goal>
</goals>
</execution>
</executions>
<configuration>
<repository>${docker.project.repository}</repository>
<tag>${project.version}</tag>
<tag>latest</tag>
<buildArgs>
<PROJECT>${project.groupId}.${project.artifactId}</PROJECT>
<SERVICE_PORT>${service.port}</SERVICE_PORT>
<JAR_FILE>${project.build.finalName}.jar</JAR_FILE>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>dev</id>
<properties>
<spring.activatedProperties>dev</spring.activatedProperties>
<vaadin.productionMode>false</vaadin.productionMode>
</properties>
</profile>
</profiles>
</project>
Upvotes: 1
Reputation: 122
As I have recently had the same struggle I have the following solution. The easiest way I found to have Java and Nodejs/NPM was to install it to the java base image. This will not be a small docker image. mvn clean package -Pproduction
the vaadin root directory. Then edit the Dockerfile to include the installation like so: https://gitlab.com/snippets/1913782
Then build and run the image. Build time and image size will increase a lot. I would love to see some suggestions or additional progress made on this process. Coming from Vaadin8 into 14 has created numerous headaches with having to learn why non-Java things are broken.
Upvotes: 0