Exclude #SpringBootApplication - java

I have 2 mains in my project
#SpringBootApplication
#ComponentScan(value = com.somepackage,
excludeFilters = {#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = Class2.class)})
public Class1
and
#SpringBootApplication
#ComponentScan(value = com.somepackage,
excludeFilters = {#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = Class1.class)})
public Class2
They are both in the same package, but need to scan some other packages.
When I run in my IDE Class1 I don't want Class2 to be loaded and when I run Class2 I don't want Class1 to be loaded.
Doing these exclusion doesn't work and doing #SpringBootApplication(exclude = Class2.class) display this error: The following classes could not be excluded because they are not auto-configuration classes
How can I exclude the other SpringBootApplication class ?
EDIT:
We are using 2 main classes because we want to have some tools (little scripts) and reuse some part of the code. Example: I have the main class of the application but I want to run some part of it with another main to do some cleanup but very rarely and don't want to setup a lot of libs just to do this. I know it's not clean, but I want to write some main rapidly to invoke my application with all the other bean loaded but not with the first main because it starts a lot of other thread that I don't need.

I think you have to change you application structure to minimal 3 modules:
Core code and resources (shared logic)
Application 1 code and resources (use the core module)
Application 2 code and resources (use the core module)
That makes thing easier and more clear. In the application modules you can implement interfaces or extends basic/default configuration from the core.
With this approach you don't have to use complex include/exclude logic for specific classes in you application main classes.

You could work with profiles using whatever name makes sense for your case.
#SpringBootApplication
#Profile("!test")
public Class1 {}
#SpringBootApplication
#Profile("test")
public Class2 {}

Related

Spring Boot: How to include a configuration class that is not in my base package?

I've got a fairly standard spring boot app which is built with gradle from several gradle modules. Here's the directory layout:
- root
- serviceA
- src/main/java
- org.example.serviceA
- ServiceAApplication.java
- serviceB
- serviceC
- common
- src/main/java
- org.example.common
- CommonSecurityConfiguration.java
What I would like to do is to include the CommonSecurityConfiguration class from the shared common module in serviceA. Note that ServiceAApplication and CommonSecurityConfiguration reside in different base packages.
I tried to use #Import(CommonSecurityConfiguration.class) on my ServiceAApplication, but that had no observable effect at all.
The only thing which worked was to annotate ServiceAApplication like so:
#SpringBootApplication(basePackages = { "org.example.serviceA", "org.example.common"})
public class ServiceAApplication { ... }
This approach works, but seems very coarse grained to me - it will import each and every component and configuration it finds in org.example.common.
Is there a better way to do this? Can I include individual classes into the component scan by listing them one by one?
Try to use
#Import(CommonSecurityConfiguration.class) above configuration class. So it would look like this:
#Configuration
#Import(CommonSecurityConfiguration.class)
public class ServiceAConfiguration { ... }
I believe what you are looking for is #CompnentScan("com.example"), this will tell Spring to look at all the files under the specified path recursively. (In this case it would be #ComponentScan("root"))
You find more info here: baeldun.com/spring-component-scanning
Hope this helps.
Since you want to control which components are brought in , we can make an annotation , let's call that annotation PickyComponentImport
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface PickyComponentImport{
}
Then on our SpringBootApplication annotation we can add a new filter which looks for this annotation.
#ComponentScan(basePackages = { "org.example.serviceA",
"org.example.common" }, includeFilters = #Filter(PickyComponentImport.class))
public class ServiceAApplication { ... }
Then we can just add that annotation on any class we want included
#Configuration
#PickyComponentImport
public class CommonSecurityConfiguration {
}
EDIT: I think if you go with this approach you can just componentScan basepackage as root.

Springboot #EntityScan not working

I have a Springboot application and my entity model is separated from my main application included as dependency.
my Application.java is located in this package com.a.b.c
#SpringBootApplication
#EntityScan("com.a.b")
public class Applciation
{
public static void main(String args[])
{
SpringApplication.run(Applciation.class, args);
}
}
and my entity model is located in another project inside this package com.a.b
But I'm getting an error: Caused by: java.lang.IllegalArgumentException: Not a managed type: class
I can relate to you. I have spent countless hours regarding this.
I'll divide your question to 3 parts (I will use "entity-project" and "spring-project" to refer to the project containing entity classes and main project trying to incorporate entity classes) :
Part 1 of 3: Making sure your entity classes are exposed in entity-project.
Create a Config at the root of entity-project
package com.a.b
#Configuration
#Import ({Entity1.class, Entity1Repo.class, Entity1Key.class,
Entity2.class, ... })
class EntityConfig {}
Part 2 of 3: Making sure your entity classes are in the classpath of spring-project.
Since you are using SpringBoot, I'm sure you are using maven (or Gradle).
Anyway, make sure you maven install the entity-project with entity classes:
cd /path/to/entity-project/
mvn install -DskipTests
In spring-project's maven file, include to the entity-project.
Part 3 of 3: Use the EntityScan annotation.
It is just my taste, but I prefer using basePackageClasses instead of basePackage.
#SpringBootApplication
#EntityScan(basePackageClasses = {Entity1.class})
// use basePackageClasses to avoid type errors
#Import({com.a.b.EntityConfig.class}) // this is what you are missing
public class Applciation
{
public static void main(String args[])
{
SpringApplication.run(Applciation.class, args);
}
}
Viola!

ClassPathScanningCandidateComponentProvider.findCandidateComponents have wrong class name

I have a java project containing a spring boot application called processor. This project depends on a project called rules and a project called service. Every project has the same package pattern - my.com.package.
The processor and rules projects both contain classes annotated with a custom annotation #Condition. The annotation interface is annotated with #Retention(RetentionPolicy.RUNTIME). When I scan for classes annotated with #Condition from service or processor like this
private ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(
false);
scanner.addIncludeFilter(new AnnotationTypeFilter(Condition.class));
for (BeanDefinition bd : scanner.findCandidateComponents("my.com")) {
try {
Class<?> c = Class.forName(bd.getBeanClassName());
Condition condition = c.getAnnotation(Condition.class);
register(condition);
} catch (ClassNotFoundException | IOException e) {
logger.error(e.getLocalizedMessage());
}
}
The classes annotated with #Condition in the processor project have the correct class name(my.com.package.x.Class), but the classes annotated with #Condition in the rules project have an incorrect fully qualified class name(my.com.Class) and it only finds 2 out of 5 class names in the project that have the annotation.
If I change the argument to scanner.findCandidateComponents to the full package path in the rules project (my.com.package.rules) while scanning in either processor or service the scanner finds no candidates. If I use my.com.* as the argument it only finds the candidates in the processor project.
I saw a similar question here Using ClassPathScanningCandidateComponentProvider with multiple jar files? and the solution was to pass the class loader to the component provider. I tried getting the class loader of the class doing the scanning and passing it to the provider like this
scanner.setResourceLoader(new PathMatchingResourcePatternResolver(classLoader));
and it didn't change any results for me.
Silly mistake, the problem was I had the wrong version of the rules project defined in the pom for my processor project so it was using an older version of the code.
However this
Condition condition = c.getAnnotation(Condition.class);
returned null for the classes taken from the jar, so this concerns me a little if this code isn't being run from source in my workspace.

How to use domain class methods on Utils or src/groovy?

I am working with grails application. I want to perform methods like get(),save(),delete(),findBy() etc and associated with domains of grails application. When I execute Domain.get() method inside Utils or src/groovy package I get following error.
Caught: java.lang.IllegalStateException: Method on class [Domain Class] was used outside of a Grails application. If running in the context of a test using the mocking API or bootstrap Grails correctly.
java.lang.IllegalStateException: Method on class [Domain Class] was used outside of a Grails application. If running in the context of a test using the mocking API or bootstrap Grails correctly.
Any one help me how can I use domain class method inside Utils or src/groovy classes?
This error is caused by interacting with GORM entities that have not been properly registered with GORM. The error doesn't have anything to do with where the interaction is initiated from (src/groovy, grails-app/services/, etc...).
Not always, but most often this error occurs in a testing environment where the entity type has not been registered with GORM.
See the project at https://github.com/jeffbrown/illegalaccessgorm. That project contains the following.
A domain class:
// grails-app/domain/demo/Person.groovy
package demo
class Person {
String name
}
A service:
// grails-app/services/demo/HelperService.groovy
package demo
class HelperService {
int getNumberOfPeople() {
Person.count()
}
}
A utility class:
// src/groovy/demo/SomeHelper.groovy
package demo
class SomeHelper {
int getNumberOfPeople() {
Person.count()
}
}
A unit test for the service:
// test/unit/demo/HelperServiceSpec.grovy
package demo
import grails.test.mixin.TestFor
import spock.lang.Specification
#TestFor(HelperService)
#Mock(Person)
class HelperServiceSpec extends Specification {
void "test gorm method"() {
expect:
service.numberOfPeople == 0
}
}
A unit test for the utility class:
// test/unit/demo/SomeHelperSpec.groovy
package demo
import spock.lang.Specification
#Mock(Person)
class SomeHelperSpec extends Specification {
void "test gorm method"() {
given:
def helper = new SomeHelper()
expect:
helper.numberOfPeople == 0
}
}
Both of those tests pass. If you remove the #Mock(Person) from either of them, the test will fail with the following:
java.lang.IllegalStateException: Method on class [demo.Person] was used outside of a Grails application. If running in the context of a test using the mocking API or bootstrap Grails correctly.
If the problem is happening in some context other than a test, knowing more about the context would be helpful. Other likely suspects are Java entities that are not being initialized properly for some reason and in some cases domain classes brought in from plugins can be problematic.
Also, knowing what version of Grails you are using may be relevant.
I hope that all makes sense.
You shouldn't use domain classes inside src/groovy or inside utils. You should use services for it. Check out best practises for Grails architecture:
Domain
Favor placing model domain specific logic in its own domain. Anything that applies to a single domain with few dependencies should go in its domain class. But keep it restricted to the logic specific to that domain only - more complex business logic that deals with a group of domains belongs to a service.
To reuse common partial queries or decompose the complex logic, use named queries and chain them together as required, just like one commonly chains jQuery function calls.
Don't mix any other common utility classes or value objects in the domain folder, rather they can go in src/groovy. If these classes need to support validation, one can annotate them with #Validateable.
If you still want to do it, check this FAQ:
import org.codehaus.groovy.grails.commons.ApplicationHolder
//…
def book = ApplicationHolder.application.getClassForName("library.Book").findByTitle("Groovy in Action")

Private class unit tests

How to unit test private (means with package visibility) classed in java?
I have a package, only one class is public in this package, other classes are private. How to cover other classed with unit tests? I would like to include unit tests in resting jar.
Create the unit test class in the same package.
For example, If com.example.MyPrivateClass located in src/main/java/com/example/MyPrivateClass.java
Then the test class will be in same package com.example.MyPrivateClassTestCase and will be located in src/test/java/com/example/MyPrivateClassTestCase.java
There are two ways to do this.
The standard way is to define your test class in the same package of the class to be tested. This should be easily done as modern IDE generates test case in the same package of the class being tested by default.
The non-standard but very useful way is to use reflection. This allows you to define private methods as real "private" rather than "package private". For example, if you have class.
class MyClass {
private Boolean methodToBeTested(String argument) {
........
}
}
You can have your test method like this:
class MyTestClass {
#Test
public void testMethod() {
Method method = MyClass.class.getDeclaredMethod("methodToBeTested", String.class);
method.setAccessible(true);
Boolean result = (Boolean)method.invoke(new MyClass(), "test parameter");
Assert.assertTrue(result);
}
}
As indicated in #Kowser's answer, the test can be in the same package.
In Eclipse, and I assume other IDEs, one can have classes in different projects but in the same package. A project can be declared to depend on another project, making the other project's classes available. That permits a separate unit test project that depends on the production project and follows its package structure, but has its own root directory.
That structure keeps the test code cleanly separated from production code.

Categories

Resources