SpringBoot "not an entity" - java

I am new to Hibernate and SpringBoot. My projects deals with a search engine that is composed of 2 independent modules + 1 base module common to both (where the IndexSetup class resides).
There is one module for indexing (JavaFx) and the other one for searching via the web browser (Spring Boot).
The indexing module involves an "IndexSetup" class that has the details on how / what should be indexed :
#Entity
#Table(name = "IndexSetups")
#Access(AccessType.PROPERTY)
public class IndexSetup {
private final SimpleIntegerProperty id = new SimpleIntegerProperty();
#Id
#GeneratedValue(strategy = GenerationType.AUTO) // For H2 AUTO is required to auto increment the id
public int getId() {
return id.get();
}
//... other properties, getters and setters
}
So it works great, the data is indexed and can be retrieved via a search method within the indexing module.
However when I run the Spring Boot server and do the same search I get
java.lang.IllegalArgumentException: Not an entity: class my.package.IndexSetup
By the way there is no build error, and before the modules were parts of a parent pom project, they were in the same project with the server class in a subfolder, and it worked. I decided to separate them for convenience during developpment and to offer two independent modules in production.
So why did it work when everything was under the same Netbeans project and now that the modules are in 2 different subfolders (but in the same group id package "my.package") I get this "Not an entity" and what should I do to solve this, where should I look at ?
Please note : I already tried this without success ("null pointer exception, cannot load the database").
Edit 1:
I also tried to add #EntityScan following this but I still get Not an entity: class my.package.IndexSetup :
#SpringBootApplication
#EntityScan( basePackages = {"my.package"} )
public class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
}
}
Edit 2 :
The architecture of the project is like :
- Parent project (my.package)
-Module Base (with IndexSetup class)
-Module Indexing (that depends on Base)
-Module Server (that also depends on Base)
The parent pom.xml reads like the following :
<?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>my.package</groupId>
<artifactId>MyApp</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!--According to https://stackoverflow.com/questions/10665936/maven-how-to-build-multiple-independent-maven-projects-from-one-project-->
<modules>
<module>Base</module> <!-- Common resources which is a dependency in Indexer and Server -->
<module>Indexer</module> <!-- Indexing part with JavaFx-->
<module>Server</module> <!-- Server (spring boot) part of -->
</modules>
<name>MyApp</name>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArguments>
<bootclasspath>${sun.boot.class.path}${path.separator}${java.home}/lib/jfxrt.jar</bootclasspath>
</compilerArguments>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.16</version>
<configuration>
<additionalClasspathElements>
<additionalClasspathElement>${java.home}/lib/jfxrt.jar</additionalClasspathElement>
</additionalClasspathElements>
</configuration>
</plugin>
</plugins>
</build>
Edit 3:
The problem originates from when the table to look at is specified:
Root<IndexSetup> from = criteriaQuery.from(IndexSetup.class);
Looking at hibernate sources not an entity is thrown whenever entityType == null. So I don't gather why the entity type is null here whereas it works outside of SpringBoot ?
Edit 4:
If I remove SpringApplication.run(ServerApplication.class, args); from the Server class' main method then the same call which was causing the issue ie :
LocalDatabase.getInstance(false) // no GUI
.getAllIndexSetups();
now works picobello. Of course it does not solve anything since I still need SpringBoot for the search! So for me it means that Spring Boot does not understand the hibernate configuration. I opened a new question to introduce the problem more accurately.
Any help appreciated,

I think you should add to your #EntityScan annotation package of your entities within second project/module

As first, some checking :
Is all your configuration only built with annotations in ServerApplication or any Java class, or are there any others external configurations in XML / YML files ? Maybe look for conflicts. We do prefer to not mix XML with annotation configuration, if possible.
Try to remove #Serializable (not really mandatory).
Try to move your entity in your root package (just as a test).
Check if the package which export #Entity is correct.
Question : what are you calling "module", it is a subpackage or Maven module or another thing ? Could we have the configuration of package names about this ?
Edit :
In the case of a multi-modules project, did you follow recommendations from spring.io about multi-modules projets ? Did you import the Spring BOM (or starter) in your submodules and did you test the Spring Boot Maven Plugin ?
Can you provide your application.properties (or application.yml whatever) with your datasource configuration ? You should check if your datasource (and JPA, driver class, ...) is correctly defined ; see spring.io

So it happened that I did not use correctly SpringBoot capabilities. Here are the steps I followed. Please remember the architecture of the project :
- Parent maven project (my.package)
|-Module Base (with IndexSetup class and [initialy] hibernate.cfg.xml in /resources. It also had in the beginning LocalDatabase class to access to the local db via hibernate)
|-Module Indexing (that depends on Base)
|-Module Server (that also depends on Base)
|-Database file (myLocalDB)
1) First I removed hibernate.cfg.xml from Base and dropped it into resources of the Indexing module. I did it because SpringBoot has its own configuration mechanism. I also removed LocalDatabase class from Base (since it would not be needed by SpringBoot) and dropped it too in the Indexing Module (where it is used indeed).
2) Following [this](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-sql.html] I added spring-boot-starter-data-jpa to the Server module pom.xml.
3) Following this tutorial I created a JPA Repository IndexSetupRepository with barely a single line of code :
public interface IndexSetupRepository extends CrudRepository<IndexSetup, Integer> {
}
4) In the Server application.properties I added those lines :
# Embedded database configuration
# The embedded database file is one level above the Server folder (ie directly within the parent project)
# we use the auto server mode to be able to use the database simultaneously in the indexer and the server
spring.datasource.url=jdbc:h2:file:../myLocalDB;AUTO_SERVER=TRUE
spring.datasource.username=myName
# This parameter helped me discover that SpringBoot was not targetting the right table name.
spring.jpa.hibernate.ddl-auto=validate
5) As SpringBoot was telling me it could not find table named index_setup (see camel case converted to _), I had to add this line to the application.properties :
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
6) Now as I got "Entity not managed", I eventually added #EntityScan annotation to the Server main class as many of you advised me to do.
#EntityScan("my.package.Entities")
Please note that #EntityScan should point to the folder containing the entity class not the entity class itself ie #EntityScan("my.package.Entities.IndexSetup") did not work.

Related

How to configure IntelliJ and hibernate-jpamodelgen annotations to launch test class from the IDE

We have a git multimodules projects MAVEN on IntelliJ. We use hibernate-jpamodelgen for criteria builder API.
We have web project using maven dependency entities library which are generating annotated class in entities => target folder.
When launching Install and test from mvn terminal it is working like a charm, but the issue is if we want to debug a test we have to launch with
-Dmaven.surefire.debug
option and with a remote application. It takes a time and not efficient. The problem is when we try to launch with right maven configuration (in IntelliJ) or directly in the class clicking the "play button" close to the method name it does not work, because it is creating in the web project all annotated classes but with empty body
for example what is in the dependency lib
#Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor", date = "2022-12-30T10:11:22.168+0400")
#SuppressWarnings({ "deprecation", "rawtypes" })
#StaticMetamodel(FixedAsset.class)
public abstract class FixedAsset_ extends com.seanergie.persistence.ObjectWithUnidAndVersion_ {
public static volatile SingularAttribute<FixedAsset, LocalDate> purchaseDate;
public static volatile SingularAttribute<FixedAsset, String> serialNumber;
public static volatile SingularAttribute<FixedAsset, MutableMoney> cost;
public static volatile SingularAttribute<FixedAsset, String> notes;
public static volatile SingularAttribute<FixedAsset, FixedAssetFilesStore> filesStore;
and what is created in other dependency target annotated class folder if i try to launch from intellij
#Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor", date = "2022-12-30T10:12:05.141+0400")
#SuppressWarnings({ "deprecation", "rawtypes" })
#StaticMetamodel(FixedAsset.class)
public abstract class FixedAsset_ extends com.seanergie.persistence.ObjectWithUnidAndVersion_ {
}
As you can see class is empty so compilation not work
We should be able to launch directly without creating a test configuration and modifying it (we have more than 100 test classes, so we can't create a conf for each one).
We see also that it try to compile all projects on each test
we tried to add in pom.xml
<dependency><!-- For launch tests directly from IntelliJ play button-->
<groupId>org.hibernate</groupId>
<artifactId>hibernate-jpamodelgen</artifactId>
<exclusions>
<exclusion>
<groupId>com.sun.activation</groupId>
<artifactId>jakarta.activation</artifactId>
</exclusion>
</exclusions>
<scope>provided</scope>
</dependency>
We also tried to deactivate or activate annotation processor in IntelliJ settings but nothing works
here in web project the hibernate mapping
<properties>
<mainClass>com.intranet.Main</mainClass>
<datasource.uri>jdbc:postgresql://localhost:5432/intranet?charSet=utf-8&amp;ApplicationName=${DATASOURCE_APPLICATION_NAME}</datasource.uri>
<datasource.pool_size.min>15</datasource.pool_size.min>
<datasource.pool_size.max>30</datasource.pool_size.max>
<hibernate.mapping.files>
<![CDATA[
<mapping-file>com/intranet-base.entities.xml</mapping-file>
<mapping-file>com/intranet-webapp.entities.xml</mapping-file>
<mapping-file>com/intranet-intranet.entities.xml</mapping-file>
<mapping-file>com/intranet-webapp.entities.xml</mapping-file>
<mapping-file>com/intranet2-intranet.entities.xml</mapping-file>
]]>
</hibernate.mapping.files>
<databaseInitializationHook.class>com.intranet.persistence.initialization.DatabaseInitializationHook</databaseInitializationHook.class>
<test.databaseInitializationHook.class>com.intranet.persistence.initialization.DatabaseInitializationHook</test.databaseInitializationHook.class>
</properties>
So finally when we launch mvn clean install even from IntelliJ (not only terminal) it create all annotated classes correctly in each library where we defined entities, but when we launch test it create another time same classes, but with empty body and in projects where entities are not defined (but use others as dependencies !)
What is the good way to make it work?
I do believe I have solved your puzzle.
The appearance of "empty" metamodel classes could be caused by one of the following:
some of plenty IDEA plugins influences on compile process and, obviously, fails
jpamodelgen annotation processor may perform extra work
And the last reason seems to be the actual one:
persistenceXml:
Per default the processor looks in /META-INF for persistence.xml. Specifying this option a persitence.xml file from a different location can be specified (has to be on the classpath)
ormXml:
Allows to specify additional entity mapping files. The specified value for this option is a comma separated string of mapping file names. Even when this option is specified /META-INF/orm.xml is implicit.
fullyAnnotationConfigured:
If set to true the processor will ignore orm.xml and persistence.xml.
I have checked and indeed placing META-INF/orm.xml with defined "foreign" entities into resources folder causes jpamodelgen to generate empty metamodel classes - as for me it looks like a bug.
You have following options (actually, depends on your project structure):
remove jpamodelgen from module dependencies if module does not define entity classes, also make sure dependency on jpamodelgen is defined as non-transitive, i.e. <scope>provided</scope>
rename extra xml mapping files - if you are on spring it is possible to specify those mappings via spring.jpa.mapping-resources
configure annotation processor as described in JBoss guide
UPD. based on #cyril comment.
First of all, defining dependencies (not imposing versions via <dependencyManagement>) in parent pom is a terrible idea - you have no chance to exclude such dependency in child modules, so, your parent pom is definitely broken.
However, I do believe you can meet your objectives (i.e. running tests via "green play button") even with broken parent pom - just follow JBoss guide and configure maven-compiler-plugin like:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerArgs>
<arg>-AfullyAnnotationConfigured=true</arg>
</compilerArgs>
</configuration>
</plugin>
IntelliJ does recognise such configuration.

Spring Boot case sensitive resolving properties from program arguments

I am trying to run a Spring boot application on my local machine. When I use the following syntax for a property it resolves well:
username: ${project.db.user}
However the following raises an exception:
username: ${PROJECT_DB_USER}
Is there any way to use uppercase here? My CI/CD pipelines defines all its variables with uppercase
I've had similar problems, and from what I've understood, since springBoot v2.1, the application.properties files don't handle environement variables well. The solution is to use Spring Profiles.
You need to add as many .properties files as you have environments, like so :
application.properties
application-staging.properties
application-prod.properties
If you have a staging and prod profiles. The .properties of the profile overrides the basic application.properties, so be sure not to forget anything in each file !
There may be other solutions as well.
You can set the profiles using various methods, in Spring boot, with Maven (sorry, the only one I'm familiar with) you can simply add it to the pom.xml like this :
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<profiles>
<profile>staging</profile>
</profiles>
</configuration>
</plugin>

Change CodeStar Spring MVC project to Spring Boot

I have a Spring Boot project that works perfectly when run in IDE. I would like to run this via AWS CodeStar. Unfortunately, the default Spring template created by CodeStar uses Spring MVC.
I cannot just overwrite the default Spring MVC project with my Spring Boot project (it doesn't work). I can copy some of my resources to the MVC project, for example index.html and that works. But then features like Thymeleaf don't work. For this and other reasons, I would like to change the provided Spring MVC into the Spring Boot structure I already have.
I followed the instructions here: https://www.baeldung.com/spring-boot-migration
Unfortunately, this doesn't help. I can create Application Entry Point and add Spring Boot dependencies without the app breaking. But when I remove the default dependencies or the configuration associated with the MVC, the app breaks. When trying to reach the URL, I get a 404 error with description:
The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.
Debugging this error message (e.g. https://www.codejava.net/java-ee/servlet/solved-tomcat-error-http-status-404-not-found) didn't help.
The message seems like it's connected to the web resource. I have my web resources in folder resources as well as webapp/resources. And Spring Boot doesn't need any location configuration, right? It uses this location by default.
Can somebody tell me what things to remove and what to add to be able to use my existing Spring Boot project?
EDIT:
This is a link to a default template for AWS CodeStar Spring web application: https://github.com/JanHorcicka/AWS-codestar-template
And this is my Spring Boot project structure:
I realize that you indicated that previously you tried to use your Spring Boot project with some modifications without success, but I think it could be actually a possibility to successfully deploy your application on AWS CodeStar, and it will be my advice.
I also realized that in your screenshot you included several of the required artifacts and classes, but please, double check that you followed these steps when you deployed your application to AWS CodeStar.
Let's start with a pristine version of your Spring Boot project running locally, without any modification, and then, perform the following changes.
First, as indicated in the GitHub link you shared, be sure that you include the following files in your project. They are required for the deployment infrastructure of AWS:
appspec.yml
buildspec.yml
template.yml
template-configuration.json
The whole scripts directory
Please, adapt any necessary configuration to your specific needs, especially, template-configuration.json.
Then, perform the following modifications in your pom.xml. Some of them are required for Spring Boot to work as a traditional deployment and others are required by the deployment in AWS CodeStar.
Be sure that you indicate packaging as war:
<packaging>war</packaging>
To ensure that the embedded servlet container does not interfere with the Tomcat to which the war file is deployed, either mark the Tomcat dependency as being provided as suggested in the above-mentioned documentation:
<dependencies>
<!-- … -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<!-- … -->
</dependencies>
Or exclude the Tomcat dependency in your pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
If necessary, apply this exclusion using some kind of profile that allows you to boot Spring Boot locally and in an external servlet container at the same time.
Next, parameterize the maven war plugin to conform to the AWS CodeStar deployment needs:
<build>
<pluginManagement>
<plugins>
<!-- ... -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
<configuration>
<warSourceDirectory>src/main/webapp</warSourceDirectory>
<warName>ROOT</warName>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
<!-- ... -->
<plugins>
</pluginManagement>
</build>
I do not consider it necessary, but just to avoid any kind of problem, adjust the name of your final build:
<finalName>ROOT</finalName>
Lastly, as also indicated in the Spring documentation, be sure that your MyProjectApplication - I assume this class is your main entry point subclass SpringBootServletInitializer and override the configure accordingly, something like:
#SpringBootApplication
public class MyProjectApplication extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(MyProjectApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(MyProjectApplication.class, args);
}
}
Please, feel free to adapt the class to your specific use case.
With this setup, try to deploy your application and see if it works: perhaps you can find some kind of library dependencies problem, but I think for the most part it should work fine.
At a first step, you can try to deploy locally the version of the application you will later deploy to AWS CodeStar following the instructions you provided in your project template, basically, once configured with the necessary changes described in the answer, by running:
mvn clean package
And deploying the generated war on your local tomcat environment. Please, be aware that probably the ROOT application already exists in a standard tomcat installation (you can verify it by inspecting the webapps folder): you can override that war file.
For local testing you can even choose a different application name (configuring build.finalName and the warName in your pom.xml file): the important thing is verify if locally the application runs successfully.
If you prefer to, you can choose to deploy the app directly to AWS CodeStar and inspect the logs later it necessary.
In any case, please, pay attention on two things: on one hand, if you have any absolute path configured in your application, it can be the cause of the 404 issue you mention in the comments. Be aware that your application will be deployed in Tomcat with context root '/'.
On the other hand, review how you configured your database access.
Probably you used application.properties and it is fine, but please, be aware that when employing the application the database must be reachable: perhaps Spring is unable to create the necessary datasources, and the persistence manager or related stuff associated with and, as a consequence, the application is not starting. Again, it may be the reason of the 404 error code.
To simplify database connectivity, for testing, at first glance, I recommend you to use simple properties for configuring your datasource, namely the driver class, connection string, username and password. If that setup works properly, you can later enable JNDI or what deemed necessary.
Remember that if you need to change your context name and/or define a datasource pool in Tomcat you can place a context.xml file under a META-INF directory in your web app root path.
This context.xml should look like something similar to:
<?xml version="1.0" encoding="UTF-8"?>
<Context path="/">
<Resource name="jdbc/myDS"
type="javax.sql.DataSource"
maxActive="100"
maxIdle="30"
maxWait="10000"
url="jdbc:mysql://localhost:3306/myds"
driverClassName="com.mysql.jdbc.Driver"
username="root"
password="secret"
/>
</Context>

Maven Multi Module Spring Boot Project

I am trying to create a multi module project that contains two module, core and web. They are both Spring Boot projects that I generated on Spring Initialzer. I setup the Maven POM files but I'm having issues getting it to deploy. However I am confused how the configuration is going to work.
The core module is going to contain the domain object / entities, Spring Data JPA repositories, services, and will be packaged as a JAR. The Web module is going to have Spring Security, the controllers, and the views. It will be packaged as a WAR.
The normal structure of a Spring Boot project looks like the following
/
pom.xml
src/
..main/
....com/
......example/
........app/
..........Application.java
..resources/
....application.properites
I essentially have two of these and two Spring Boot application / configuration / initialization classes.
My questions are
Do the properties have to live in a single configuration file or can I have two application.properities, one the core jar, and one for the WAR?
Can I have the following in my core.jar
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class TimesheetCoreApplication {
public static void main(String[] args) {
SpringApplication.run(TimesheetCoreApplication.class, args);
}
}
Along with the following two in my web.war
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class TimesheetWebApplication {
public static void main(String[] args) {
SpringApplication.run(TimesheetWebApplication.class, args);
}
}
AND
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
public class ServletInitializer extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(TimesheetWebApplication.class);
}
}
Since Spring Boot does a lot of auto configuration, will they step over each other with the configuration, either conflicting or overriding one another?
What is the best way to approach this? I would like to leverage Spring Boot if possible.
your design is slightly incorrect, if your core project is going to have domain entities, repositories etc. it need not be a BOOT Application.
As you said, this is going to be a Jar.
Now you make the your web application a Boot Application with dependency on the core module jar. You can define .properties or .yml in the Boot Application and you are good to go.
The problem with Spring Initializer is, it gives you an impression that all the code need to reside under a single project or module.
This is typical project I follow for my Applications
project-core-module (packaging Jar -> domain entities, repositories etc)
project-service-module (boot -> configuration, rest interface, security etc)
project-system-tests (runs on compile time and certify the build)
Now you bind core and system module with service module as dependency.
Please find answers to your questions
1. Do the properties have to live in a single configuration file or can I have two application.properities, one the core jar, and one for the WAR?
They cannot both be named application.properties. Refer my answer to this question here.
2. Can I have the following in my core.jar
Yes you can, if you follow the steps mentioned in the link.
3. Since Spring Boot does a lot of auto configuration, will they step over each other with the configuration, either conflicting or overriding one another?
You will only ever boot one application at a time. Say if you are running the TimesheetWebApplication application then the TimesheetCoreApplicationwill not be booted. The time TimesheetCoreApplication will only be included as a normal JAR.
In addition you can also do the following
Package your TimesheetCoreApplicationas follows. Refer here. This will make sure that every time you do maven install, the jar which will only contain the class files(No tomcat embedded libraries) will be installed to repo.
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<classifier>exec</classifier>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
Briefly,
You don't need .properties , you can use yaml with spring boot, it's more readable.Single yaml file is enough for your configuration,because yaml uses indents, so you can distinguish parameters easily.
Single Application class is fine, because you won't execute only core module.
There are lots of starter dependencies and their configurations , you can speed up in projects.

Java Annotation processor for remote JAR

General question
I have two projects A and B; B has a dependency on A. I want to generate some code in B with an Annotation Processor, based on annotations on objects in A. When I run the compilation with the correct Processor implementation, only the annotated objects from B are picked up.
I understand that scanning other JARs must be disabled by default, because you usually don't want to do an annotation scan for all your dependencies. I also understand that it may be impossible to do what I want to do because of compiler magic - which I don't know a lot about - but I'm hoping it's not.
Specific case
My projects are called DB and WEB. WEB obviously depends on DB for its JPA access; this is configured in Maven. Due to a number of architectural choices, DB must remain a separate JAR. DB doesn't use Spring except for some annotations which are consumed by WEB; WEB uses Spring MVC.
I'm trying to generate the CrudRepository interfaces for all my JPA entities with an Annotation Processor. The #Repository objects are supposed to go in a repo package in the WEB project, so they can be used with #Autowired wherever in my WEB application. The annotation I'm performing the scan for is #javax.persistence.Entity, but I've also tried a custom annotation, with the same results.
#SupportedAnnotationTypes("javax.persistence.Entity")
#SupportedSourceVersion(SourceVersion.RELEASE_8)
public class RepositoryFactory extends AbstractProcessor {
#Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (Element e : roundEnv.getElementsAnnotatedWith(Entity.class)) {
if (e.getKind() != ElementKind.CLASS) {
continue;
}
// TODO: implement logic to skip manually implemented Repos
try {
String name = e.getSimpleName().toString();
TypeElement clazz = (TypeElement) e;
JavaFileObject f = processingEnv.getFiler().
createSourceFile("blagae.web.repo." + name + "Repo");
try (Writer w = f.openWriter()) {
PrintWriter pw = new PrintWriter(w);
pw.println("package blagae.web.repo;");
pw.println("import org.springframework.data.repository.CrudRepository;");
pw.printf("import %s;\n", clazz.toString());
pw.println("import org.springframework.stereotype.Repository;");
pw.println("#Repository");
pw.printf("public interface %sRepo extends CrudRepository<%s, Long> {}\n", name, name);
pw.flush();
}
} catch (IOException ex) {
Logger.getLogger(RepositoryFactory.class.getName()).log(Level.SEVERE, null, ex);
}
}
return false;
}
}
Ideally, I'd love for someone to tell me about an annotation that would be as simple as
#ComponentScan(basePackages = "blagae.db.*")
But of course, I'm not counting on that because it would probably be documented somewhere. As a workaround, I could just add the Spring dependency to the db and generate the classes there, but they only serve a purpose in the Spring MVC app. I'm also wary of the config it might take to make this work.
UPDATE
Some extra info: I'm using the maven-processor-plugin, which I've verified to work well in the WEB project for classes that are defined there. However, I specifically want access classes annotated in the dependency project DB. I have looked into the method AbstractProcessor::getSupportedOptions but it's unclear to me what I could do there.
Maven config:
<plugin>
<groupId>org.bsc.maven</groupId>
<artifactId>maven-processor-plugin</artifactId>
<version>2.2.4</version>
<configuration>
<processors>
<processor>blagae.utils.RepositoryFactory</processor>
</processors>
</configuration>
<executions>
<execution>
<id>process</id>
<goals>
<goal>process</goal>
</goals>
<phase>generate-sources</phase>
</execution>
</executions>
</plugin>
SUGGESTION
Another random thought I had would be to run a JavaCompiler process for the DB project in WEB, but how would I inject my Processor ?
Annotation processor works on compilation phase of your project (WEB in your case) and compiler compiles this project. Dependencies of current project are already compiled and compiler (and as result your annotation processor) don't touch (or have no access) third party libraries (DB).
You can try to extract annotation processor into separate project/jar and use it in WEB and DB projects. In this case annotation processor will create CrudRepository on compilation phase of concrete project. And all generated classes in DB project will be available in WEB.
Personally, I would extract the annotation processor in a separate maven module and add a dependency to it from the WEB module.
However, this doesn't matter that much for successfully triggering an annotation processor.
In order to have an annotation processor working, there are two things you have to provide:
a class that extends the javax.annotation.processing.AbstractProcessor class.
a special file, nested in the META-INF/services of the project.
Since you mentioned that currently no classes are generated, I would assume that you're missing the meta file. So, open your WEB project and navigate to src/main/resouces folder. Within, you have to create a META-INF folder with a nested services folder in it. Then, in services create a file, named javax.annotation.processing.Processor. The content of the file should list the fully-qualified class name(s) of your annotation processor(s). If there is more than one annotation processor, the fully-qualified class-names should be on separate lines. But since you have just one, you'd have something like:
com.yourdomain.processor.RepositoryFactory
Note that you will have to change this line with the actual fully-quallified class-name of your annotation processor.
In the end, you should end up with similar structure:
This meta file is important, because otherwise the compiler is not aware of the user-defined annotation processors. Having it, it will make use of all the registered processors.
After that, when you do a mvn clean install all your modules will be cleaned and built. However, since the compiler will now be aware of your annotation processor, it will trigger it. All the generated sources will be located (by default) in the target/generated-sources folder. Moreover, they will all be under the package you've configured in the annotation process, i.e. blagae.web.repo.
In order to use the generated sources within your code, you will have to add the targer/generated-sources to the project classpath. If you don't want to rely on the IDE to do this, you can extend the maven <build> by adding the target/generated-sources to the classpath. Something like:
<build>
<resources>
...
<resource>
<directory>${project.build.directory}/generated-resources</directory>
</resource>
</resources>
</build>
In your project A include the META-INF/beans.xml file,
which will contain following:
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
version="1.1" bean-discovery-mode="all">
</beans>
and give it a try. You should use JavaEE 7/CDI 1.1. Refer to Java EE 7 Deployment Descriptors for more information.
You can also refer to this related question: How to #Inject object from different project module included as jar

Categories