0

I have a main spring boot application that connects to a mongodb to perform certain actions.

It's main class:

@EnableMongoRepositories(repositoryBaseClass = CustomMongoRepositoryImpl.class)
@ComponentScan(basePackages = {"org.company.product"})
@SpringBootApplication
public class SvcPosControllerDataApplication {
    public static void main(String[] args) {
        SpringApplication.run(SvcPosControllerDataApplication.class, args);
    }
}

It's pom.xml

...
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
      <scope>runtime</scope>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-configuration-processor</artifactId>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
    <!-- Database -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-mongodb</artifactId>
    </dependency>
...

It's application properties

...
spring.data.mongodb.uri=mongodb://localhost:27017/
spring.data.mongodb.database=myMongoDb_Test
...

Now, I want to be able and use (on demand) an external jar file that connects to an mssql database and retrieves some data.

I am adding the external jar into my main project in the following way (I can not declare it as a dependency):

enter image description here

The external jar file has the following elements:

A repository:

@Repository
public interface CustomerRepository extends JpaRepository<Customer, String> {
    Customer findByAccountNumber(String accountNumber);
}

An entity class:

@Entity
@Table(name = "Customer")
public class Customer {
    @Column(name = "StoreID")
    private Integer storeID;

    @Id
    @Size(max = 20)
    @Nationalized
    @Column(name = "AccountNumber", length = 20)
    private String accountNumber;
    ...

A data service to fetch some data from the database (implements a common interface and is declared as primary to override main's project dataservice):

@Service
@Primary
public class BasicDataServiceCustomer_External implements IDataServiceCustomer {
    @Autowired
    CustomerMappingService customerMappingService;
    @Autowired
    Utils utils;
    @Autowired
    CustomerRepository repo;
    @Autowired
    EntityManager em;

    @Override
    public List<Customer> findAll() {
    ...

It's pom.xml

...
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>${spring-boot-starter.version}</version>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
    <version>${spring-boot-starter-data-jpa.version}</version>
</dependency>

<dependency>
    <groupId>com.microsoft.sqlserver</groupId>
    <artifactId>mssql-jdbc</artifactId>
    <version>${mssql-jdbc.version}</version>
    <scope>runtime</scope>
</dependency>
...

And it's application properties:

spring.datasource.url= jdbc:sqlserver://localhost:1433;encrypt=true;trustServerCertificate=true;databaseName=MyDatabase_Test
spring.datasource.username= sa
spring.datasource.password= *******

spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.SQLServerDialect
spring.jpa.hibernate.ddl-auto= none
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.properties.hibernate.create_empty_composites.enabled=true

Finally it's main class:

//@SpringBootApplication
@EnableJpaRepositories(basePackages = {"org.company.product.external_service.repository"})
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(basePackages = {"org.company.product","org.company.product.external_service"})
public class ScvExternalDataService02 {
    public static void main(String[] args) {
        SpringApplication.run(ScvExternalDataService02.class, args);
    }
}

But I am getting an exception:

2023-05-19 11:44:51.159 ERROR 23888 --- [  restartedMain] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'svcPosControllerDataApplication': Unsatisfied dependency expressed through field 'dataServiceFactory'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'dataServiceFactory': Unsatisfied dependency expressed through field 'basicDataServices'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'basicDataServiceCustomer_Custom_02': Lookup method resolution failed; nested exception is java.lang.IllegalStateException: Failed to introspect Class [org.company.product.main_service.service.impl.BasicDataServiceCustomer_Custom_02] from ClassLoader [jdk.internal.loader.ClassLoaders$AppClassLoader@1d44bcfa]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:659)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:639)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:308)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295)
    at org.company.product.main_service.SvcPosControllerDataApplication.main(SvcPosControllerDataApplication.java:23)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'dataServiceFactory': Unsatisfied dependency expressed through field 'basicDataServices'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'basicDataServiceCustomer_Custom_02': Lookup method resolution failed; nested exception is java.lang.IllegalStateException: Failed to introspect Class [org.company.product.main_service.service.impl.BasicDataServiceCustomer_Custom_02] from ClassLoader [jdk.internal.loader.ClassLoaders$AppClassLoader@1d44bcfa]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:659)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:639)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1391)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1311)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:656)
    ... 25 common frames omitted
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'basicDataServiceCustomer_Custom_02': Lookup method resolution failed; nested exception is java.lang.IllegalStateException: Failed to introspect Class [org.company.product.main_service.service.impl.BasicDataServiceCustomer_Custom_02] from ClassLoader [jdk.internal.loader.ClassLoaders$AppClassLoader@1d44bcfa]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:289)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineConstructorsFromBeanPostProcessors(AbstractAutowireCapableBeanFactory.java:1302)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1219)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1609)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1573)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1462)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1349)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1311)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:656)
    ... 39 common frames omitted
Caused by: java.lang.IllegalStateException: Failed to introspect Class [org.company.product.main_service.service.impl.BasicDataServiceCustomer_Custom_02] from ClassLoader [jdk.internal.loader.ClassLoaders$AppClassLoader@1d44bcfa]
    at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:485)
    at org.springframework.util.ReflectionUtils.doWithLocalMethods(ReflectionUtils.java:321)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:267)
    ... 54 common frames omitted
Caused by: java.lang.NoClassDefFoundError: javax/persistence/criteria/Expression
    at java.base/java.lang.Class.getDeclaredMethods0(Native Method)
    at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3402)
    at java.base/java.lang.Class.getDeclaredMethods(Class.java:2504)
    at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:467)
    ... 56 common frames omitted
Caused by: java.lang.ClassNotFoundException: javax.persistence.criteria.Expression
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
    ... 60 common frames omitted

First I want to know if this can be done (load everything required for mssql connection, repository etc. from within the external jar).

If yes, then how can I achieve such a scenario?

(*) I understand that a configuration that manages all the required data source connections (mongodb, mssql) and their dependencies from within the main service might make more sense. But this is a requirement asked for investigation purposes.

thanili
  • 777
  • 4
  • 26
  • 57
  • It is absolutly possible. In general it is achived by adding libraries in classpath (as described here https://stackoverflow.com/questions/8084926/including-jar-files-in-class-path ). – Ivan Baranuk May 19 '23 at 11:46
  • Thanks @IvanBaranuk , I think I have done this (for intellij) as you can see in the screenshot I have attached but it does not seem to work. Unless I am missing something here – thanili May 22 '23 at 07:30

1 Answers1

0

You can add your external library to the classpath but there is another way that is better. Because of both projects are maven based it's better to use one of them as a dependency of another one . If we name the project that manages database connection as ConnectorProject you should at first build this project . After building ,the jar file of this project goes to .m2 folder so every projects that add ConnectorProject dependency in its own pom.xml file can use it. So add this dependency to your main project.

<dependency>
    <groupId>${connector-project-groupId}</groupId>
    <artifactId>${connector-project-artifactId}</artifactId>
    <version>${connector-project-version}</version>
</dependency>