0

I have some project trying to build a microservices architecture. Some of the projects are used producing jar files (I refer these projects as "library projects".) for the callable microservices. One of these microservices are responsible for user/customer registration and lookup. All the data is persisted in a Cassandra cluster. One of the library projects are responsible for providing various Cassandra based services/value objects, etc...

In the microservice pom file, I include the jar produced by the library project. It compiles, but when I start the customer service the classes from that jar are not found.

***************************
APPLICATION FAILED TO START
***************************

Description:

Field repository in com.besztercekk.tao.customer.handler.PostUserHandler required a bean of type 'com.besztercekk.tao.cassandra.repository.ReactiveUserRepository' that could not be found.

The injection point has the following annotations:
    - @org.springframework.beans.factory.annotation.Autowired(required=true)


Action:

Consider defining a bean of type 'com.besztercekk.tao.cassandra.repository.ReactiveUserRepository' in your configuration.

Here are some code snippets, but I can attach anything else you need.

This is the pom file for the microservice. The class what is not found is in this dependency: tao-elszamolas-cassandra.

<?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>

    <artifactId>tao-elszamolas-customer-service</artifactId>
    <packaging>jar</packaging>

    <name>tao-elszamolas-customer-service</name>
    <description>TAO elszamolas customer backend service</description>

    <parent>
        <groupId>com.besztercekk.tao</groupId>
        <artifactId>tao-elszamolas-spring</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath> 
    </parent>

    <dependencies>
        <!-- Spring Cloud Eureka client -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <!-- Spring Cloud config client -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-client</artifactId>
        </dependency>

        <!-- Webflux -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- Reactive cassandra -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-cassandra-reactive</artifactId>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>27.1-jre</version>
        </dependency>

        <!-- TAO elszamolas dependencies -->
        <dependency>
            <groupId>com.besztercekk.tao</groupId>
            <artifactId>tao-elszamolas-cassandra</artifactId>
        </dependency>

        <dependency>
            <groupId>com.besztercekk.tao</groupId>
            <artifactId>tao-elszamolas-jwt</artifactId>
        </dependency>
    </dependencies>


    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <release>11</release>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

This is the pom file for the project what contains the missing class:

<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>

    <artifactId>tao-elszamolas-cassandra</artifactId>
    <packaging>jar</packaging>

    <name>tao-elszamolas-cassandra</name>
    <description>TAO elszámolás Spring alapú több projektben használt komponensek gyűjtő projektje</description>

    <parent>
        <groupId>com.besztercekk.tao</groupId>
        <artifactId>tao-elszamolas-spring</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <dependencies>
        <!-- Reactive cassandra -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-cassandra-reactive</artifactId>
        </dependency>
    </dependencies>

</project>

Here the class where the error is coming from:

@Component
@Validator(UserDataValidator.class)
public class PostUserHandler extends AbstractValidationHandler<User, ChainableValidator> {

    @Autowired
    private ReactiveUserRepository repository;

    @Autowired
    private PasswordEncoder passwordEncoder;

    public PostUserHandler() {
        super(User.class);
    }

    @Override
    protected Mono<ServerResponse> processRequest(User body, ServerRequest request) {
        return repository.save(enrichUser(body, UUIDs.random().toString()))
                .flatMap(updatedUser -> {
                    sendRegistrationCode();
                    return ok(updatedUser);
                })
                .onErrorResume(IllegalArgumentException.class, e -> ServerResponse.badRequest().build())
                .switchIfEmpty(ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR).build());
    }

    @Override
    protected Mono<User> extractContent(ServerRequest request) {
        return request.bodyToMono(User.class);
    }

    private void sendRegistrationCode() {
        //TODO: Send Kafka message to email component
    }

    private Mono<ServerResponse> ok(User c) {
        return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromPublisher(Mono.just(c), User.class));
    }

    private User enrichUser(User user, String activationCode) {
        return User.builder()
                .authorities(user.getAuthorities())
                .email(user.getEmail())
                .enabled(user.isEnabled())
                .firstName(user.getFirstName())
                .id(UUIDs.timeBased())
                .lastName(user.getLastName())
                .middleName(user.getMiddleName())
                .password(passwordEncoder.encode(user.getPassword()))
                .registrationTime(LocalDateTime.now())
                .passwordExpiryTime(LocalDateTime.now())
                .roles(user.getRoles())
                .username(user.getUsername())
                .accountExpired(false)
                .accountLocked(false)
                .activationCode(activationCode)
                .build();
    }

}

Any help would be appreciated!

UPDATE (SOME EXTRA INFO ADDED):

Here is the main class containing the component scan.

@SpringBootApplication
@EnableEurekaClient
@ComponentScan(basePackages = {"com.besztercekk.tao.customer", "com.besztercekk.tao.cassandra"})
public class CustomerBackendApplication {

    public static void main(String[] args) {
        SpringApplication.run(CustomerBackendApplication.class, args);
    }   
}

Here is the repository class:

package com.besztercekk.tao.cassandra.repository;

import java.util.UUID;

import org.springframework.data.cassandra.repository.ReactiveCassandraRepository;
import org.springframework.stereotype.Component;

import com.besztercekk.tao.cassandra.model.User;

import reactor.core.publisher.Mono;

/**
 * This interface provides reactive {@link User} services.
 */
public interface ReactiveUserRepository extends ReactiveCassandraRepository<User, UUID> {

    Mono<User> findByUsername(String username);

    Mono<User> findByEmail(String email);
}

Also tried to add @Component and @Repository annotations to this interface, just to see its effect. :-)

My other idea was add the bean programatically to the context after restart. But this is just an interface, so not sure if it works out at all.

The third idea was wrap this interface into a service class and that is easy to add programatically.

But I really want it to be added automatically, without writing code.

This is the package hierarchy in the library project

bkk
  • 307
  • 5
  • 22
  • show your repository – Ryuzaki L May 12 '19 at 16:53
  • 1
    What you posted shows that there is a missing Spring bean of type ReactiveUserRepository, not that a class is not found. Post the exact and complete stack trace, and explain why you think a Spring bean of type ReactiveUserRepository should exist. – JB Nizet May 12 '19 at 17:05
  • @Deadpool: It just lives on local at the moment. – bkk May 12 '19 at 19:13
  • @JB Nizet: Sorry. You are right. The bean is missing. I think it should be in the context, because the dependency is in the pom and it is in the referenced jar file on a path which is "component scanned". Pls, have a look on the update. I add my application class, what contains the component scan annotation, and I also add the repository class well. Thanks for the help!! – bkk May 12 '19 at 19:24

1 Answers1

0

Try adding the following annotation to any of the @Configuration classes:

@EnableReactiveCassandraRepositories
@Configuration
class MyConfiguration() {
 //code 
}

The above code enables spring to automatically scan and create the default implementation bean for your interface ReactiveUserRepository .

Also annotate the interface like :

@Repository
public interface ReactiveUserRepository extends ReactiveCassandraRepository<User, UUID> {

    Mono<User> findByUsername(String username);

    Mono<User> findByEmail(String email);
}

For more details read : https://dzone.com/articles/reactive-streams-with-spring-data-cassandra

Also, if you would like more details on the annotations read a similar post :

how annotation @Repository in java spring work?

Ananthapadmanabhan
  • 5,706
  • 6
  • 22
  • 39