Spring Boot Integration

Spring Framework Logo 2018

EclipseStore comes with a Spring Boot integration. It is available within this artifact:

pom.xml
<dependencies>
   <dependency>
      <groupId>org.eclipse.store</groupId>
      <artifactId>integrations-spring-boot3</artifactId>
      <version>2.1.0</version>
   </dependency>
</dependencies>

Official Spring Boot site: https://spring.io/projects/spring-boot

The integration requires Spring Boot 3.x.

Configuration

The configuration of the StorageManager can be done using key/value pairs that are provided by Spring Boot external Configuration. The configuration keys must be prefixed by org.eclipse.store

org.eclipse.store.storage-directory=/opt/data/eclipse-store-storage
org.eclipse.store.channel-count=2

The list of all EclipseStore configuration properties and their meaning are listed on our documentation page.

The configuration values are handled using the typesafe configuration approach, and you can read these values by accessing the EclipseStoreProperties Spring bean. The Spring Boot integration provides a set of Bean Factories which read the configuration and initialize the relevant EclipseStore facilities for you, including EmbeddedStorageManager.

StorageManager injection

The simplest way to use this integration is when you can externalize all the configuration using Spring configuration files. Subsequently, you just create a Spring Boot application and add a dependency on the integration. Then, in the Bean where you need access to the StorageManager, you add the @Autowired annotation and a StorageManager is created for you. All configuration values are loaded from the external configuration and the StorageManager is already initialized and ready to use.

Example:

@SpringBootApplication
public class SomeSpringApplication
{
    public static void main(String... args)
    {
        SpringApplication.run(SomeSpringApplication.class, args);
    }
}

@Component
public class JokesStorageImpl implements JokesStorage
{
    private final EmbeddedStorageManager storageManager;

    // Inject the StorageManager (Constructor injection)
    public JokesStorageImpl(EmbeddedStorageManager storageManager)
    {
        this.storageManager = storageManager;
    }

    // Implement the JokesStorage interface
}

Configuration Injection

In case you need to modify the configuration in some way in the program, change something, add and the like, you can have the EclipseStoreProperties Spring bean injected into your class. This class contains all the configuration values that are used to create a StorageManager. You can modify these values and then use the EmbeddedStorageManagerFactory Bean to create a new EmbeddedStorageManager or EmbeddedStorageFoundationFactory to create a new EmbeddedStorageFoundation.

    @Bean
    EmbeddedStorageManager injectStorageTest(
        @Autowired EclipseStoreProperties myConfiguration,
        @Autowired EmbeddedStorageManagerFactory managerFactory,
        @Autowired EmbeddedStorageFoundationFactory foundationFactory
   ) {
        // Modify the configuration
        myConfiguration.setStorageDirectory(temp.getDir().getAbsolutePath());
        // Create a new StorageFoundation
        EmbeddedStorageFoundation<?> storageFoundation = foundationFactory.createStorageFoundation(myConfiguration);

        // Modify the storageFoundation
        //storageFoundation.onConnectionFoundation(f -> f.someOperation);

        // Create a new StorageManager
        return managerFactory.createEmbeddedStorageManager(storageFoundation, true);
    }

Autostart

  • The StorageManager is already started unless you specified the configuration value org.eclipse.store.auto-start=false.

  • If you have used the org.eclipse.store.root configuration item, the StorageManager is already associated with an instance of that class as the Root object. This class must have a default or public no-argument constructor.

It is also possible to obtain the entire configuration within the StorageManagerConfiguration Bean, enabling you to directly create a foundation and storage manager. This can be helpful if you need to stop storage at runtime and then restart it.

Disable Auto StorageManager creation

If the user does not define any beans of type EmbeddedStorageFoundation and EmbeddedStorageManager, these beans are automatically created. This behavior can be changed using the following configuration.

org.eclipse.store.auto-create-default-storage=false
org.eclipse.store.auto-create-default-foundation=false

Multiple Storage Managers

You can have a more than one Storage Manager in your application. This can be useful if you want to have different storage targets for different data. For example, you might want to have a datastore for your user data and another for your product data. You can do this by creating multiple configuration classes and multiple Storage Managers.

@Configuration
public class TwoBeanConfiguration
{

    @Autowired
    private EmbeddedStorageFoundationFactory foundationFactory;
    @Autowired
    private EmbeddedStorageManagerFactory managerFactory;

    @Bean("first_config")
    @ConfigurationProperties("org.eclipse.store.first")
    EclipseStoreProperties firstStoreProperties()
    {
        return new EclipseStoreProperties();
    }

    @Bean("second_config")
    @ConfigurationProperties("org.eclipse.store.second")
    EclipseStoreProperties secondStoreProperties()
    {
        return new EclipseStoreProperties();
    }

    @Bean
    @Qualifier("first_storage")
    EmbeddedStorageManager createFirstStorage(@Qualifier("first_config") final EclipseStoreProperties firstStoreProperties) {
      return managerFactory.createStorage(
          foundationFactory.createStorageFoundation(firstStoreProperties),
          firstStoreProperties.isAutoStart()
      );
    }

    @Bean
    @Qualifier("second_storage")
    EmbeddedStorageManager createSecondStorage(@Qualifier("second_config") final EclipseStoreProperties secondStoreProperties) {
      return managerFactory.createStorage(
          foundationFactory.createStorageFoundation(secondStoreProperties),
          secondStoreProperties.isAutoStart()
      );
    }

}

The configuration properties are defined in the application.properties file with appropriate prefixes.

org.eclipse.store.first.storage-directory=${java.io.tmpdir}/${random.int}
org.eclipse.store.first.auto-start=false
org.eclipse.store.first.root=org.eclipse.store.integrations.spring.boot.types.storages.FirstRoot

org.eclipse.store.second.storage-directory=${java.io.tmpdir}/${random.int}
org.eclipse.store.second.auto-start=false
org.eclipse.store.second.root=org.eclipse.store.integrations.spring.boot.types.storages.SecondRoot

Mutex Locking

EclipseStore supports mutex locking. This is useful if you have multiple processes that need to access the same objects. Easiest way to use it is to use the annotation @read and @write on the methods that need to be locked. The annotation @read is used for methods that only read data and @write is used for methods that modify data.

To activate mutex locking, you need to add the following dependency to Maven pom.xml of your SpringBoot application:

pom.xml
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>

Then you can use the Mutex Locking in your code:

@Component
public class SomeStorageImpl implements SomeStorage
{
    @Read
    public String getSomethingById(Integer Id)
    {
        // Read something from the storage
    }

    @Write
    public void addSomething(String something)
    {
        // Add something to the storage
    }
}

If you have larger object graph, you can consider to add more Mutexes and lock different parts of the object graph. This can be done by adding the @Mutex annotation to the class. The parameter of this annotation is the name of the mutex. If you have multiple classes with the same mutex name, they will be locked together.

@Component
@Mutex("jokes")
public class JokesStorageImpl implements JokesStorage
{
}

Logging

EclipseStore Spring module supports standard Spring logging, so you can add this into your config: logging.level.org.eclipse.store=debug to obtain all EclipseStore configuration keys:

15:57:34.923 [main] DEBUG o.e.s.i.s.b.t.EclipseStoreProviderImpl -- EclipseStore configuration items:
15:57:34.923 [main] DEBUG o.e.s.i.s.b.t.EclipseStoreProviderImpl -- storage-directory : jokes_storage
15:57:34.923 [main] DEBUG o.e.s.i.s.b.t.EclipseStoreProviderImpl -- channel-count : 2
15:57:34.923 [main] DEBUG o.e.s.i.s.b.t.EclipseStoreProviderImpl -- auto-start : true

Key values containing "password" are replaced by "xxxxx".

Register JDK 17 and JDK 8 Handlers

Handler for JDK 17 are registered automatically. Handler for JDK 8 are not registered automatically, because of the backwards compatibility. If you create a new storage you can enable these handlers. If you have an existing storage created with JDK8 handlers, you have to active it too. If you need to register JDK 8 handlers, you can do it by adding the following code to your configuration:

org.eclipse.store.register-jdk8-handlers=true

Activating REST Service SpringBoot

If you are interested in using the EclipseStore REST service in your application, consider to include the SpringBoot REST Service. Detailed documentation can be found at Setup of Spring Boot REST Service.

Activating Client GUI

You can include the Client GUI application to be directly as a module served by your application. For this purpose, include the SpringBoot Console to your application classpath. We assume that you are starting Spring MVC in your application already.

Please add the following dependency to Maven pom.xml of your SpringBoot application:

<dependency>
    <groupId>org.eclipse.store</groupId>
    <artifactId>integrations-spring-boot3-console</artifactId>
    <version>{maven-version}</version>
</dependency>

In order to operate properly, Client GUI requires a working SpringBoot REST Service. This is already prepackaged in the console module and needs to be activated. In addition, the path to the Client GUI needs to be configured.

Please set the following properties in your configuration file:

org.eclipse.store.rest.enabled=true
vaadin.url-mapping=/store-console/*

The Client GUI will start with your application and will be available at the specified URL. So if your SpringBoot application starts a web server on port 8080 the Client GUI will be available at: http://localhost:8080/store-console/. Open this URL in your browser, select from the dropdown list the endpoint of the Spring REST Service (http://localhost:8080/store-data/) and click connect.

Spring Dev Tools

If you are using Spring Dev Tools, it is recommended to exclude the EclipseStore classes from the classpath. Spring Dev Tools use their own restart classloader, which dynamically reloads classes. This can cause issues with the EclipseStore classes, as they are not designed to be reloaded on the fly.

Code Configuration Before Storage Creation

There are some use cases where it is necessary to configure the storage’s context before it is created. For this purpose, you can use the StorageContextInitializer to create a custom storage configuration in your application.

Example for configuring LazyReferenceManager:

@Component
public class StorageContextInitializerImpl implements StorageContextInitializer
{
    @Override
    public void initialize()
    {
        LazyReferenceManager.set(LazyReferenceManager.New(
                Lazy.Checker(
                        1_000_000, // timeout of lazy access
                        0.75       // memory quota
                )));
    }
}