Search code examples
spring-bootspring-dataspring-data-gemfire

Error creating bean with name 'gemfireCache': FactoryBean threw exception on object creation


I am trying to create an "employee" Region and put some data into it. But, I am getting Exception below:

[warn 2018/12/27 17:15:46.518 IST tid=0x1] Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'gemfireConfiguration': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'gemfireCache': FactoryBean threw exception on object creation; nested exception is java.lang.NoClassDefFoundError: it/unimi/dsi/fastutil/ints/Int2ObjectOpenHashMap

[warn 2018/12/27 17:15:46.519 IST tid=0x1] Invocation of destroy method failed on bean with name 'gemfireCache': org.apache.geode.cache.CacheClosedException: A cache has not yet been created.

[error 2018/12/27 17:15:46.522 IST tid=0x1] Caught exception while allowing TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener@c667f46] to prepare test instance [com.gemfire.demo.Gemfire1ApplicationTests@48bfb884]

Domain class

 @Region("employee")
    public class Employee {
            @Id
            public String name;
            public double salary;
            ...
    }

Repository class

@Repository
public interface EmployeeRepository extends CrudRepository<Employee, String> {

    Employee findByName(String name);
}

Configuration class

@Configuration
@ComponentScan
@EnableGemfireRepositories(basePackages = "com.gemfire.demo")
public class GemfireConfiguration {

    @Autowired
    EmployeeRepository employeeRepository;

    @Bean
    Properties gemfireProperties() {
        Properties gemfireProperties = new Properties();
        gemfireProperties.setProperty("name", "SpringDataGemFireApplication");
        gemfireProperties.setProperty("mcast-port", "0");
        gemfireProperties.setProperty("log-level", "config");
        return gemfireProperties;
    }

    @Bean
    @Autowired
    CacheFactoryBean gemfireCache() {
        CacheFactoryBean gemfireCache = new CacheFactoryBean();
        gemfireCache.setClose(true);
        gemfireCache.setProperties(gemfireProperties());
        return gemfireCache;
    }

    @Bean(name="employee")
    @Autowired
    LocalRegionFactoryBean<String, Employee> getEmployee(final GemFireCache cache) {
        LocalRegionFactoryBean<String, Employee> employeeRegion = new LocalRegionFactoryBean<String, Employee>();
        employeeRegion.setCache(cache);
        employeeRegion.setClose(false);
        employeeRegion.setName("employee");
        employeeRegion.setPersistent(false);
        employeeRegion.setDataPolicy(DataPolicy.PRELOADED);
        return employeeRegion;
    }

} 

POM.XML

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.logging.log4j</groupId>
                    <artifactId>log4j-to-slf4j</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-core</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
         <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-gemfire</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.9.0</version>
        </dependency>  

Solution

  • Adding additional tips with your above GemFire/Spring JavaConfig configuration class above.

    Given you are using Spring Data Kay (implied by your use of the Spring Boot 2.0.x parent POM, i.e. org.springframework.boot:spring-boot-dependencies; see here), then you could be using Spring Data GemFire's (relatively) new and convenient Annotation-based configuration model.

    By doing so, your GemfireConfiguration class above would become...

    @PeerCacheApplication
    @EnableGemfireRepositories(basePackages = "com.gemfire.demo")
    class GemfireConfiguration {
    
    
        @Bean(name="employee")
        LocalRegionFactoryBean<String, Employee> getEmployee(GemFireCache cache) {
    
            LocalRegionFactoryBean<String, Employee> employeeRegion = 
                new LocalRegionFactoryBean<String, Employee>();
    
            employeeRegion.setCache(cache);
            employeeRegion.setClose(false);
            employeeRegion.setDataPolicy(DataPolicy.PRELOADED);
    
            return employeeRegion;
        }
    }
    

    A few things to keep in mind:

    1. @PeerCacheApplication is meta-annotated with @Configuration so you do not need the explicit Spring @Configuration annotation on the configuration class.

    2. @PeerCacheApplication allows you to adjust the GemFire log-level (along with other logging configuration) using the logLevel annotation attribute. Similarly, you can set the log-level using the corresponding property, spring.data.gemfire.cache.log-level in a Spring Boot application.properties file (see here). There are many other attributes and corresponding properties (e.g. name) you can use to adjust and customize other configuration.

    3. While String-based package names are supported on @EnableGemfireRepositories and similar annotations, we generally prefer and recommend users to use the type-safe variant basePacakgeClasses. You only need to refer to a single type from each top-level package where your application Repositories are kept.

    4. The explicit @Autowired annotation is not needed on your bean definitions. You do not need to explicit inject the EmployeeRepository in the configuration class to have it initialized; just inject it into the @Service class where it will be used.

    5. For convenience, the name ("employee") of the Region bean definition on your LOCAL "employee" Region, will also be used as the name of the Region, so employeeRegion.setName("employee") is unnecessary.

    6. You should not combine LocalRegionFactoryBean.setPersistent(:boolean) with LocalRegionFactoryBean.setDataPolicy(:DataPolicy) since the DataPolicy is going to take precedence.

    7. While @ComponentScan is perfectly acceptable and even convenient in development, I generally do not prefer nor recommend users to use component-scanning. It is usually always better to be explicit.

    8. As stated in the comments, you chould remove <relativePath/> from your parent definition in your application Maven POM file.

    9. Final note, as of this post, Spring Boot 2.0.8.RELEASE is the latest release.

    As for your classpath issues, if you are using Maven correctly, then Maven should take care of pulling in the correct transitive dependencies.

    You can refer to the many examples I have in this repo for further clarification.

    Hope this helps!