I'm using Spring Data Rest to expose rest endpoints which i can use in my user interface. However, during testing I noticed that when hitting the base rest url (http://localhost:8080/rest) , the endpoints are inconsistently exposed. I'm using the Annotation based RepositoryDetectionStrategies.
I would appreciate some assistance in understanding and resolving the issue.
Example:
First boot: all endpoints are properly exposed:
{
"_links" : {
"orders" : {
"href" : "http://localhost:8080/rest/orders{?page,size,sort}",
"templated" : true
},
"reports" : {
"href" : "http://localhost:8080/rest/reports{?page,size,sort}",
"templated" : true
},
"buySells" : {
"href" : "http://localhost:8080/rest/positions{?page,size,sort}",
"templated" : true
},
"profile" : {
"href" : "http://localhost:8080/rest/profile"
}
}
}
After a restart without any code or configuration changes:
{
"_links" : {
"orders" : {
"href" : "http://localhost:8080/rest/orders{?page,size,sort}",
"templated" : true
},
"buySells" : {
"href" : "http://localhost:8080/rest/positions{?page,size,sort}",
"templated" : true
},
"profile" : {
"href" : "http://localhost:8080/rest/profile"
}
}
}
As you can see, the reports endpoint is not exposed after the restart of the application. The exposure failure is very inconsistent; sometimes all 3 endpoints are not exposed, sometimes 1 or 2 are missing (inconsistent which) and sometimes everything is fine.
I'm not able to find any failures in the logs, even when putting it to TRACE.
Configuration
Here is the main pom:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.rvh</groupId>
<artifactId>project-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>project-parent</name>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<slf4j.version>1.7.30</slf4j.version>
</properties>
<modules>
<!-- <module>compiler-plugin-java-9</module> --> <!-- We haven't upgraded to java 9. -->
<module>web</module>
<module>api-engine</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>nl.rvh</groupId>
<artifactId>business-rule-validator</artifactId>
<version>1.0</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-maven-plugin</artifactId>-->
<!-- <version>2.5.3</version>-->
<!-- </dependency>-->
</dependencies>
</dependencyManagement>
<dependencies>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-devtools</artifactId>-->
<!-- <optional>true</optional>-->
<!-- </dependency>-->
</dependencies>
<build>
<finalName>collector</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<configuration>
<mainClass>com.rvh.collector.CollectorApplication</mainClass>
</configuration>
</execution>
</executions>
</plugin>
<!-- mvn sonar:sonar \-->
<!-- -Dsonar.projectKey=baralga \-->
<!-- -Dsonar.organization=baralga \-->
<!-- -Dsonar.host.url=https://sonarcloud.io \-->
<!-- -Dsonar.login=<GENERATED_TOKEN>-->
<plugin>
<groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<version>3.7.0.1746</version>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.6</version>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>prepare-package</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<useSystemClassLoader>false</useSystemClassLoader>
<argLine>-Dfile.encoding=UTF8</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
here is the web module pom
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.rvh</groupId>
<artifactId>project-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>collector</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>collector</name>
<description>collector application</description>
<packaging>war</packaging>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
<m2eclipse.wtp.contextRoot>/</m2eclipse.wtp.contextRoot>
<java.version>11</java.version>
<sonar.coverage.jacoco.xmlReportPaths>${project.build.directory}/site/jacoco/jacoco.xml
</sonar.coverage.jacoco.xmlReportPaths>
</properties>
<dependencies>
<!-- Compile -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-data</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-devtools</artifactId>-->
<!-- <optional>true</optional>-->
<!-- </dependency>-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-media</artifactId>
<version>17-ea+14</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>com.rvh.api.engine</groupId>
<artifactId>apiEngine</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- Provided -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>nl.rvh.trade</groupId>
<artifactId>kraken-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>nl.rvh</groupId>
<artifactId>business-rule-validator</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
This is how the main class is annotated:
@Configuration
@ComponentScan("com.rvh.**")
@EnableAutoConfiguration
@EnableJpaRepositories("com.rvh.collector")
@EnableScheduling
public class CollectorApplication {
this is the rest config class:
@Configuration
public class RestRepositoryConfig implements RepositoryRestConfigurer {
@Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config, CorsRegistry cors) {
//Only expose annotated repositories
config.setRepositoryDetectionStrategy(RepositoryDetectionStrategy.RepositoryDetectionStrategies.ANNOTATED);
config.setBasePath("/rest");
ExposureConfiguration exposureConfiguration = config.getExposureConfiguration();
exposureConfiguration.withItemExposure((metadata, httpMethods) -> httpMethods
.disable(HttpMethod.PATCH, HttpMethod.DELETE, HttpMethod.TRACE, HttpMethod.HEAD, HttpMethod.PUT, HttpMethod.POST));
}
}
and my rest repo's, which are all part of the com.rvh.collector.rest package.
@RepositoryRestResource
@PreAuthorize("hasRole('ROLE_ADMIN')")
public interface OrdersRest extends PagingAndSortingRepository<Order, Integer> {
}
@RepositoryRestResource(path = "positions")
@PreAuthorize("hasRole('ROLE_ADMIN')")
public interface PositionRestRepo extends PagingAndSortingRepository<BuySell, Integer> {
}
@RepositoryRestResource
@PreAuthorize("hasRole('ROLE_ADMIN')")
public interface ReportsDao extends PagingAndSortingRepository<Report, Long> {
@Query("select report from Report report where report.accountId in "
+ "(select acc.id from User user join user.accounts acc where user.userName = ?#{ principal?.username }) ")
Iterable<Report> findAll();
@Query("select report from Report report where report.accountId in "
+ "(select acc.id from User user join user.accounts acc where user.userName = ?#{ principal?.username })"
+ "and report.id = :aLong ")
Optional<Report> findById(Long aLong);
}