Reading multiple entity scan packages in spring boot application - spring

I have Spring boot application
#SpringBootApplication
#EntityScan(basePackages = {"${scan.packages}"})
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
While reading multiple entity scan packages from one property separated by comma like below?
scan.packages=com.mycompany.model.package1 ,
com.mycompany.model.package2
I got this exception :
java.lang.IllegalArgumentException: Unknown entity:
com.mycompany.model.package2.Myclass

You can scan multiple Entity like this
#EntityScan(basePackages= {"scan.packages1","scan.packages2"})

This should work
#EntityScan(basePackages = {"#{'${scan.packages}'.split(',')}"})

According to the EntityScan annotation javadoc there are three ways to define the packages where to scan for entities:
value, alias for basePackages: #EntityScan( {"scan.packages1","scan.packages2"})
basePackages, alias for value: #EntityScan(basePackages= {"scan.packages1","scan.packages2"}).
basePackagesClasses, the type safe version: #EntityScan(basePackages=E.class}.Where E was a marker interface just to underline the Entities. Please see the code bellow. It could also be an annotation.
interface E {
}
#Entity
public class Glass implements E {
// Typical code to be added here
}
Or as an annotation:
#Retention(RetentionPolicy.RUNTIME)
#interface E {
}
#Entity
#E
public class Glass implements E {
// Typical code to be added here
}
From my perspective I would choose either using directly value or basePackageClasses. If I can something easier to read I would do that, and I think that is what value does, while the basePackageClasses introduces the added benefit of type safety and I can see multiple reasons to go for that. It all depends on the context.

I hit the same problem and posted an issue on the Spring Boot issue tracker about it.
The answer was that
... As shown by #25436, #EntityScan did support property solution in 2.3 so, if we decide to reinstate support, we might want to consider how to handle multi-value properties.
So it appears some Spring Boot version did support it, but then the support was dropped again... for more info, check the Spring Boot issue linked in the quote.

try this :
#EntityScan(basePackages= {"${scan.packages1","scan.packages2}"})

Related

Load custom properties file in Spring Boot MVC Main

I have created a myApp.properties in resources folder location and mentioned the server.port in this file.
myApp.properties
myApp.server.port=8020
Now I want to read load this property into my application. But I have to read this before I actually a server.
Here I am trying to do like this
#SpringBootApplication
#ComponentScan(basePackages = {"com.myorg.myapp" })
#EnableConfigurationProperties
#PropertySource("classpath:myApp.properties")
#Component
public class MyAppApplication {
#Value("${myApp.server.port}")
private static String serverPort;
public static void main(String[] args) throws Exception{
try {
SpringApplication appCtxt = new SpringApplication(MyAppApplication.class);
appCtxt.setDefaultProperties(Collections
.singletonMap("server.port", serverPort));
appCtxt.run(args);
} catch (Exception e) {
e.printStackTrace();
}
}
But serverPort is coming as null.
I also tried to create a separate Config file like this but it can't be accessed in static main
#Configuration
#PropertySource("myApp.properties")
#ConfigurationProperties
public class MyAppConfig {
#Value("${myApp.server.port}")
private String serverPort;
/**
* #return the serverPort
*/
public String getServerPort() {
return serverPort;
}
}
Any suggestion would be helpful.
Spring boot injects properties during the initialization of the application context.
This happens (gets triggered) in the line:
appCtxt.run(args);
But you try to access the property before this line - that why it doesn't work.
So bottom line, using "#Value" in the main method doesn't work and it shouldn't.
Now from the code snippet, it looks like you could merely follow the "standards" of spring boot and create the file application.properties with:
server.port=1234
The process of starting the embedded web server in spring boot honors this property and bottom line it will have the same effect and Tomcat will be started on port 1234
Update 1
Based on OP's comment:
So, how can I have multiple application.properties.
In the Spring Boot's documentation it is written that application.properties are resolved from the classpath. So you can try the following assuming you have different modules A,B,C and web app D:
Create src/main/resources/application.properties inside each of 4 modules and pack everything together. The configuration values will be merged (hopefully they won't clash)
If you insist on naming properties A.properties, B.properties and C.properties for each of non-web modules, you can do the following (I'll show for module A, but B and C can do the same).
#Configuration
#PropertySource("classpath:A.properties")
public class AConfiguration {
}
Create in Module A: src/main/resources/A.properties
If you need to load the AConfiguration automatically - make the module A starter (using autoconfig feature of spring-boot):
Create src/resources/META-INF/spring.factories file with the following content:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
<package_of_AConfiguration>.AConfiguration
Also this has been the requirement to separate C from entire bundle where it might run as bundle for some and as a separate for some others
Although I haven't totally understood the requirement, but you can use #ConditionalOnProperty for configuration CConfiguration (that will be created just like AConfiguration.java in my previous example) but this times for module C.
If the conditional is met, configuration will run and load some beans / load its own properties or whatever. All in all conditionals (and in particular Profiles in spring) can help to reach the desired flexibility.
By default, the application.properties file can be used to store property pairs, though you can also define any number of additional property files.
If you save myApp.server.port=8020 in application.properties, it will work fine.
To register a custome property file, you can annotate a #Configuration class with the additional #PropertySource annotation:
#Configuration
#PropertySource("classpath:custom.properties")
#PropertySource("classpath:another.properties")
public class ConfigClass {
// Configuration
}
make sure, your class path is correct.

Spring data with multiple modules not working

I'm trying to set up a project with two data sources, one is MongoDB and the other is Postgres. I have repositories for each data source in different packages and I annotated my main class as follows:
#Import({MongoDBConfiguration.class, PostgresDBConfiguration.class})
#SpringBootApplication(exclude = {
MongoRepositoriesAutoConfiguration.class,
JpaRepositoriesAutoConfiguration.class
})
public class TemporaryRunner implements CommandLineRunner {
...
}
MongoDBConfiguration:
#Configuration
#EnableMongoRepositories(basePackages = {
"com.example.datastore.mongo",
"com.atlassian.connect.spring"})
public class MongoDBConfiguration {
...
}
PostgresDBConfiguration:
#Configuration
#EnableJpaRepositories(basePackages = {
"com.example.datastore.postgres"
})
public class PostgresDBConfiguration {
...
}
And even though I specified the base packages as described in documentation, I still get those messages in the console:
13:10:44.238 [main] [] INFO o.s.d.r.c.RepositoryConfigurationDelegate - Multiple Spring Data modules found, entering strict repository configuration mode!
13:10:44.266 [main] [] INFO o.s.d.r.c.RepositoryConfigurationExtensionSupport - Spring Data MongoDB - Could not safely identify store assignment for repository candidate interface com.atlassian.connect.spring.AtlassianHostRepository.
I managed to solve this issue for all my repositories by using MongoRepository and JpaRepository but AtlassianHostRepository comes from an external lib and it is a regular CrudRepository (which totally makes sense because the consumer of the lib can decide what type of DB he would like to use). Anyway it looks that basePackages I specified are completely ignored and not used in any way, even though I specified com.atlassian.connect.spring package only in #EnableMongoRepositories Spring Data somehow can't figure out which data module should be used.
Am I doing something wrong? Is there any other way I could tell spring data to use mongo for AtlassianHostRepository without changing the AtlassianHostRepository.class itself?
The only working solution I found was to let spring data ignore AtlassianHostRepository (because it couldn't figure out which data source to use) then create a separate configuration for it, and simply create it by hand:
#Configuration
#Import({MongoDBConfiguration.class})
public class AtlassianHostRepositoryConfiguration {
private final MongoTemplate mongoTemplate;
#Autowired
public AtlassianHostRepositoryConfiguration(final MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
#Bean
public AtlassianHostRepository atlassianHostRepository() {
RepositoryFactorySupport factory = new MongoRepositoryFactory(mongoTemplate);
return factory.getRepository(AtlassianHostRepository.class);
}
}
This solution works fine for a small or limited number of repositories used from a library, it would be rather cumbersome to create all the repositories by hand when there are more of them, but after reading the source code of spring-data I see no way to make it work with basePackages as stated in documentation (I may be wrong though).

SpringBoot scanBasePackages not working in multi module project

I have the following maven project structure:
eu.arrowhead
common
repository
-AJpaRepository.class
orchestrator
controller
-AController.class
OrchestratorApplication
other_modules...
Where two of the modules are common, and orchestrator. Common is a dependency for the Orchestrator module. The JpaRepositoryClass is annotated with #Repository.
In the controller class I use the constructor autowiring to get a copy of the repository:
private final AJpaRepository serviceRepo;
#Autowired
public AController(AJpaRepository serviceRepo){
this.serviceRepo = serviceRepo;
}
And finally, in the Application class, I use scanBasePackages, to pick up the components from the common module:
#SpringBootApplication(scanBasePackages = "eu.arrowhead")
public class OrchestratorApplication {
public static void main(String[] args) {
SpringApplication.run(OrchestratorApplication.class, args);
}
}
When I start the application, I get:
Description:
Parameter 0 of constructor in eu.arrowhead.orchestrator.controller.ArrowheadServiceController required a bean of type 'eu.arrowhead.common.repository.ArrowheadServiceRepo' that could not be found.
Action:
Consider defining a bean of type 'eu.arrowhead.common.repository.ArrowheadServiceRepo' in your configuration.
If I use scanBasePackages = {"eu.arrowhead.common"} then the application starts without an error, but I can not reach the endpoint in my controller class (getting the default 404 error). If I write scanBasePackages = {"eu.arrowhead.common", "eu.arrowhead.orchestrator"} it's the same as if only "eu.arrowhead" is there, I get the same error at startup.
Is this how this supposed to work? I highly doubt it.
Depencendies:
Common module: starter-data-jpa, starter-json, mysql-connector-java, hibernate-validator
Orch module: starter-web, the common module.
I also tried using #ComponentScan, but had the same result. What is the problem? Thanks.
You are missing #EnableJpaRepositories("eu.arrowhead") annotation to enable Spring Data JPA repository scanning.

Choose component loading order

I have a spring boot with three #Component classes.
src.main.java
|_components
|_A
|_B
|_C
I need B and C executed before A can be executed. How do I specify that?
Should I use #DependsOn annotation? Or #Order? All answers online are for #Configuration and #Bean classes
You should be able to use #DependsOn like this:
#DependsOn({"b", "c"})
#Component("a")
public class A {
}
#Component("b")
public class B {
}
#Component("c")
public class C {
}
You can use #Order(<int>) notation when your components are advice and you want one advice to run before another advice.In that case the lower the number, the higher would be the precedence.
E.g.
Advice #Order(5) will have higher precedence over #Order(10).

Spring data rest - Is there a way to restrict the supported operations?

I want to expose data from a database as Restful APIs in a Spring(SpringBoot) application. Spring Data Rest appears to be an exact fit for purpose for this activity.
This database is read-only for my application needs. The default provides all the HTTP methods. Is there a configuration that I can use to restrict (in fact prevent) the other methods from being exposed?
From the Spring docs on Hiding repository CRUD methods:
16.2.3. Hiding repository CRUD methods
If you don’t want to expose a save or delete method on your
CrudRepository, you can use the #RestResource(exported = false)
setting by overriding the method you want to turn off and placing the
annotation on the overriden version. For example, to prevent HTTP
users from invoking the delete methods of CrudRepository, override all
of them and add the annotation to the overriden methods.
#RepositoryRestResource(path = "people", rel = "people")
interface PersonRepository extends CrudRepository<Person, Long> {
#Override
#RestResource(exported = false)
void delete(Long id);
#Override
#RestResource(exported = false)
void delete(Person entity);
}
It is important that you override both delete methods as the exporter
currently uses a somewhat naive algorithm for determing which CRUD
method to use in the interest of faster runtime performance. It’s not
currently possible to turn off the version of delete which takes an ID
but leave exported the version that takes an entity instance. For the
time being, you can either export the delete methods or not. If you
want turn them off, then just keep in mind you have to annotate both
versions with exported = false.
As of early 2018, there is now the ability to only expose repository methods explicitly declared for exposure (DATAREST-1176)
See RepositoryRestConfiguration
A Export false at Type level does not allow overriding with export true at Method level ticket (DATAREST-1034) was opened, but closed as a duplicate of DATAREST-1176. Oliver Gierke stated:
I'll resolve this as fixed against the version of DATAREST-1176 for
now but feel free to reopen in case there's anything else you need.
They are not exact duplicates and the functionality described in 1034 would have been more user friendly, but there are at least some options now.
By default, Spring boot exposes all methods to REST. You can set that to false.
config.setExposeRepositoryMethodsByDefault(false);
For more information, you can refer org.springframework.data.rest.core.config.RepositoryRestConfiguration.
Sample code snippet to do this:
#Configuration
public class ApplicationRepositoryConfig implements RepositoryRestConfigurer {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config, CorsRegistry cors) {
..........
config.setExposeRepositoryMethodsByDefault(false);
}
}
Since Spring Data REST 3.1, we can configure exposure per HTTP method. I used the following snippet to disable exposure of PUT, PATCH, POST and DELETE methods for items and collections:
#Component
public class SpringDataRestCustomization implements RepositoryRestConfigurer {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config, CorsRegistry cors) {
ExposureConfiguration exposureConfiguration = config.getExposureConfiguration();
exposureConfiguration.withItemExposure((metadata, httpMethods) -> httpMethods.disable(HttpMethod.PUT)
.disable(HttpMethod.PATCH).disable(HttpMethod.POST).disable(HttpMethod.DELETE))
.withCollectionExposure((metadata, httpMethods) -> httpMethods.disable(HttpMethod.PUT)
.disable(HttpMethod.PATCH).disable(HttpMethod.POST).disable(HttpMethod.DELETE));
}
}

Resources