Distributed transaction managing in sharding environment - spring-boot

I'm using two datasources named mainDB and ShardDB. ShardDB is consist of 4 databases, and I'm trying to create method that write something in mainDB and ShardDB_1 and ShardDB_2. This method should be run in atomically.
How can I make this atomic function?
What I tried was using chaindTransactionManager, but It only manages a single connections per datasource.(manages a mainDB connection and one of ShardDB connection)
#Bean("userHikariDataSource")
fun userDataSource(): DataSource? {
val shardSize = user.shards.size
val router = UserDataSourceRouter(
shardSize,
user.virtualShardSize,
user.virtualShards
)
val dataSourceMap: MutableMap<Any, Any> = LinkedHashMap()
// sharding
for (i in 0 until user.shards.size) {
val shard: UserShardingDataSourceProperty.Shard = user.shards[i]
val masterDataSourceName =
i.toString() + DataSourceKey.DATASOURCE_DELIMITER + MultiDataSource.USER + DataSourceKey.DATASOURCE_DELIMITER + shard.master.name
val masterDs: DataSource =
dataSource(shard.username, shard.password, shard.master.url, masterDataSourceName)
dataSourceMap[masterDataSourceName] = masterDs
for (slave in shard.slaves) {
val slaveDataSourceName =
i.toString() + DataSourceKey.DATASOURCE_DELIMITER + MultiDataSource.USER + DataSourceKey.DATASOURCE_DELIMITER + slave.name
val slaveDs: DataSource = dataSource(shard.username, shard.password, slave.url, slaveDataSourceName)
dataSourceMap[slaveDataSourceName] = slaveDs
}
}
router.setTargetDataSources(dataSourceMap)
router.afterPropertiesSet()
userDataSourceRouter = router
return LazyConnectionDataSourceProxy(router)
}
#Bean(name = ["userHikariEntityManager"])
fun entityManagerFactory(#Qualifier("userHikariDataSource") dataSource: DataSource): LocalContainerEntityManagerFactoryBean {
val entityManager = LocalContainerEntityManagerFactoryBean()
entityManager.dataSource = dataSource
val vendorAdapter = HibernateJpaVendorAdapter()
vendorAdapter.setDatabase(jpaVendorAdapterProperty.database)
vendorAdapter.setShowSql(jpaVendorAdapterProperty.showSql)
vendorAdapter.setDatabasePlatform(jpaVendorAdapterProperty.databasePlatform)
entityManager.jpaVendorAdapter = vendorAdapter
entityManager.setPackagesToScan("com.kakaoenterprise.kakaowork.gate.message")
entityManager.persistenceUnitName = "userHikariEntityManager"
return entityManager
}
#Bean(name = ["userHikariTransactionManager"])
fun userTransactionManager(
#Qualifier("userHikariEntityManager") entityManagerFactory: EntityManagerFactory
): PlatformTransactionManager {
val transactionManager = JpaTransactionManager()
transactionManager.entityManagerFactory = entityManagerFactory
return transactionManager
}
private fun dataSource(username: String, password: String, url: String, resourceName: String): DataSource {
val config = commonHikariConfig.hikariConfig()
config.jdbcUrl = url
config.username = username
config.password = password
return HikariDataSource(config)
}
I'm using above as datasource config.

Related

How to get a data from mongoDB with the _id in Kotlin

I'm new in programming and I'm had having some problems to create an API for Kotlin getting data from a existing DB from mongoDB to my API.
I'm making Tests and I want to return a data with it Id, but I can't.
I'm using the SpringBoot, JPA, and mongoDB Data. The host is my machine, the database name is ArthurLeywin, and the collection name is Capitulos.
I'm receiving the error status=404, error=Not Found, path=/ArthueLeywin/Capitulos/6306e6417d5b2e259951f292}
The code of the Test is
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ExtendWith(SpringExtension::class)
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TbateMobileReederApplicationTests #Autowired constructor(
private val capituloRepository: CapituloRepository,
private val restTemplate: TestRestTemplate
) {
private val defaultCapituloId : ObjectId = ObjectId("6306e6417d5b2e259951f292")
//ObjectId.get()
#LocalServerPort
protected var port: Int = 27017
private fun getRootUrl(): String? = "http://localhost:$port/ArthueLeywin/Capitulos"
#Test
fun `should return single cap by id`() {
val response = restTemplate.getForObject(
getRootUrl() + "/$defaultCapituloId",
Object::class.java
)
val titleResponse = response.toString()
println(titleResponse)
}
}
My controller, if it's needed is
#RestController
#RequestMapping("/Capitulos")
class CapituloController (
private val capituloRepository : CapituloRepository
){
#GetMapping
fun getAllCapitulos() : ResponseEntity<List<Capitulo>>{
val capitulos = capituloRepository.findAll()
return ResponseEntity.ok(capitulos)
}
#GetMapping("/{id}")
fun getOneCapitulo(#PathVariable("id") id: String): ResponseEntity<Capitulo> {
val capitulo = capituloRepository.findOneById(ObjectId(id))
return ResponseEntity.ok(capitulo)
}
#PostMapping
fun createCapitulo (#RequestBody request: CapituloRequest) : ResponseEntity<Capitulo>{
val capitulo = capituloRepository.save(Capitulo(
nomeCapitulo = request.nomeCapitulo,
urlString = request.urlString,
novelTexto = request.novelTexto
))
return ResponseEntity(capitulo, HttpStatus.CREATED)
}
}
As I said I'm new in this, so please help me with explanations <3

use #Bean inject a bean but could't pass check about #ConditionalOnBean

When I comment out the line #ConditionalOnBean(name = "customRedisConnectionFactory"),the function is execute and the customRedisConnectionFactory is the object that I injected. But when I enabled this line, the method doesn't execute. I want to know the reason. Can someone help me answer it.
RedisConfig
#Configuration
#ConditionalOnClass(RedisOperations.class)
#AutoConfigureBefore({CacheAutoConfiguration.class})
public class RedisConfig {
#Bean(name = "customRedisConnectionFactory")
public RedisConnectionFactory customRedisConnectionFactory(){
RedisStandaloneConfiguration redisStandaloneConfiguration=new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setDatabase(12);
redisStandaloneConfiguration.setHostName("127.0.0.1");
redisStandaloneConfiguration.setPassword("yichen");
redisStandaloneConfiguration.setPort(6379);
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(redisStandaloneConfiguration);
return jedisConnectionFactory;
}
}
CustomRedisCacheManagerConfiguration
#Configuration
#AutoConfigureAfter({CacheAutoConfiguration.class})
#ConditionalOnClass(RedisOperations.class)
#EnableConfigurationProperties({RedisProperties.class, CacheProperties.class, RedisCacheExpiresProperties.class})
public class CustomRedisCacheManagerConfiguration {
private final CacheProperties cacheProperties;
public CustomRedisCacheManagerConfiguration(CacheProperties cacheProperties) {
this.cacheProperties = cacheProperties;
}
#Bean(name = "serviceRedisCacheManager")
#ConditionalOnBean(name = "customRedisConnectionFactory")
public RedisCacheManager serviceRedisCacheManager(
#Qualifier("customRedisConnectionFactory") RedisConnectionFactory customRedisConnectionFactory,
RedisCacheExpiresProperties redisCacheExpiresProperties) {
RedisCacheManager.RedisCacheManagerBuilder builder =
RedisCacheManager.builder(customRedisConnectionFactory).cacheDefaults(determineConfigurationDefault());
Map<String, Long> cacheConfigurations = redisCacheExpiresProperties.getCacheExpires();
if (cacheConfigurations != null && cacheConfigurations.size() > 0) {
Map<String, RedisCacheConfiguration> redisCacheConfigurations = new HashMap<>();
for (String cacheName : cacheConfigurations.keySet()) {
Assert.notNull(cacheName, "CacheName must not be null!");
long ttl = cacheConfigurations.get(cacheName);
Assert.isTrue(ttl > 0, "Expire must not be null!");
RedisCacheConfiguration redisCacheConfiguration = determineConfiguration(cacheName, ttl);
redisCacheConfigurations.put(cacheName, redisCacheConfiguration);
}
builder.withInitialCacheConfigurations(redisCacheConfigurations);
}
return builder.build();
}
private org.springframework.data.redis.cache.RedisCacheConfiguration determineConfigurationDefault() {
CacheProperties.Redis redisProperties = this.cacheProperties.getRedis();
org.springframework.data.redis.cache.RedisCacheConfiguration config =
org.springframework.data.redis.cache.RedisCacheConfiguration.defaultCacheConfig();
config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
if (redisProperties.getTimeToLive() != null) {
config = config.entryTtl(redisProperties.getTimeToLive());
}
if (redisProperties.getKeyPrefix() != null) {
config = config.prefixKeysWith(redisProperties.getKeyPrefix());
}
if (!redisProperties.isCacheNullValues()) {
config = config.disableCachingNullValues();
}
if (!redisProperties.isUseKeyPrefix()) {
config = config.disableKeyPrefix();
}
return config;
}
private org.springframework.data.redis.cache.RedisCacheConfiguration determineConfiguration(String cacheName,long ttl) {
org.springframework.data.redis.cache.RedisCacheConfiguration config =
org.springframework.data.redis.cache.RedisCacheConfiguration.defaultCacheConfig();
config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
config = config.entryTtl(Duration.ofSeconds(ttl));
config = config.disableCachingNullValues();
return config;
}
}
application.properties
spring.redis.password=yichen
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.database=10
spring.redis.max.idle=10
spring.redis.max.total=30
spring.redis.max.wait.mills=-1
spring.cache.redis.time-to-live=60000
spring.cache.redis.key-prefix=test
spring.cache.redis.cache-expires.c3m=180
spring.cache.redis.cache-expires.c5m=300
spring.cache.redis.cache-expires.c10m=600
spring.cache.redis.cache-expires.c30m=1800
spring.cache.redis.cache-expires.c24h=86400
spring.cache.redis.cache-expires.c7d=604800
spring.cache.redis.cache-expires.c30d=2592000
RedisCacheExpiresProperties
#ConfigurationProperties(prefix = "spring.cache.redis")
public class RedisCacheExpiresProperties {
private Map<String, Long> cacheExpires;
public Map<String, Long> getCacheExpires() {
return cacheExpires;
}
public void setCacheExpires(Map<String, Long> cacheExpires) {
this.cacheExpires = cacheExpires;
}
}
I think it's maybe a problem with the order the two configuration class (RedisConfig and CustomRedisCacheManagerConfiguration) were auto configured. Make sure you registered the class as auto configuration class and RedisConfig is auto configured before CustomRedisCacheManagerConfiguration.
When I try to replace #DependsOn("customRedisConnectionFactory") with #ConditionalOnBean(type = "RedisConnectionFactory"), it successfully executed.
Their differences and implementation details are to be studied.

Spring boot circular reference with multi-tenant db setup

It's another one of those circular reference errors in Spring Boot. My log says:
userDetailsServiceImpl defined in file [/home/.../auth/UserDetailsServiceImpl.class]
┌─────┐
| tenantDataSource
└─────┘
Now, I've seen this happening to other people, but never with one class. Here comes the TenantDataSource:
#Component
class TenantDataSource(private val configRepository: DataSourceConfigRepository): Serializable {
private val dataSources = HashMap<String, DataSource>()
val hexKeyMaterials = HashMap<String, String>()
private val logger: Logger = LoggerFactory.getLogger(this::class.java)
fun getDataSource(dataSourceName: String): DataSource {
logger.info(" [X] TenantDataSource - Getting DataSource: $dataSourceName")
if (!dataSources.contains(dataSourceName)) {
logger.info(" [X] TenantDataSource - DataSource $dataSourceName is not in map... Creating...")
dataSources[dataSourceName] = createDataSource(dataSourceName)
}
return dataSources[dataSourceName]!!
}
#PostConstruct
fun getAll(): Map<String, DataSource> {
logger.info(" [X] TenantDataSource - Processing all datasources after construct")
val configList: List<DataSourceConfig> = configRepository.findAll()
val result: MutableMap<String, DataSource> = HashMap()
for (config in configList) {
result[config.name] = getDataSource(config.name)
hexKeyMaterials[config.name] = config.keymaterial
}
return result
}
private fun createDataSource(dataSourceName: String): DataSource {
logger.info(" [X] TenantDataSource - Creating dataSource from config: $dataSourceName")
val config: DataSourceConfig = configRepository.findByName(dataSourceName)
return DataSourceBuilder
.create()
.username(config.username)
.password(config.password)
.url(config.url).build()
}
}
I can't imagine where I should start looking for the circular reference in this... :( I've seen some people saying "i don't even have constructor injection..." Well, I do because I don't like the autowired lateinit variables in Kotlin, that's all. So if there's a chance I'd like to keep my CIs. Help me out here because I don't get what's going on! :)
Oh, the UserDetailsServiceImpl if it helps:
#Service
class UserDetailsServiceImpl(private val userRepository: UserRepository, private val decrypter: HaliteDecrypter, private val tenantDataSource: TenantDataSource): UserDetailsService {
private val logger: Logger = LoggerFactory.getLogger(this.javaClass)
override fun loadUserByUsername(username: String): AuthUser {
logger.info("Trying to get user for authentication $username")
val user = userRepository.getUserByUsername(username)
decrypter.hexKeyMaterial = tenantDataSource.hexKeyMaterials[TenantContext.currentTenant.get()]!!
user?.let {
logger.info("Returning UserDetails for " + user.username)
return AuthUser(
user.userId,
user.username,
decrypter.decryptPasswd(user.password).toString(Charsets.US_ASCII),
user.role.id,
user.role.name,
user.flags
)
} ?: throw UsernameNotFoundException(String.format("Username %s not found", username))
}
}

AWS Spring can't set object mapper

im trying to set jackson serializer at spring MessageTemplate to send messages alredy formated using jacksonMapper and SQS/SNS, the problem is that it keeps sending messages unformated, even with notation #JsonProperty or #JsonFormat
here is my configs:
#Configuration
class AwsMessagingConfigurationLocal(
#Value("\${cloud.aws.region.static}") val region: String,
#Value("\${cloud.aws.credentials.accessKey}") val accessKey: String,
#Value("\${cloud.aws.credentials.secretKey}") val secretKey: String,
#Value("\${cloud.aws.endpoint}") val endpoint: String
) {
#Bean
fun notificationMessageTemplate(): NotificationMessagingTemplate {
return NotificationMessagingTemplate(buildAmazonSNSAsync())
}
#Bean
fun queueMessagingTemplate(): QueueMessagingTemplate {
return QueueMessagingTemplate(buildAmazonSQSAsync())
}
#Bean
fun simpleMessageListenerContainerFactory(): SimpleMessageListenerContainerFactory {
val messageListenerContainerFactory = SimpleMessageListenerContainerFactory()
messageListenerContainerFactory.setAmazonSqs(buildAmazonSQSAsync())
return messageListenerContainerFactory
}
#Bean
fun queueMessageHandlerFactory(objectMapper: ObjectMapper): QueueMessageHandlerFactory {
val messageConverter = MappingJackson2MessageConverter()
messageConverter.objectMapper = objectMapper.registerModule(JavaTimeModule())
val queueMessageHandlerFactory = QueueMessageHandlerFactory()
queueMessageHandlerFactory.messageConverters = listOf(messageConverter)
queueMessageHandlerFactory.setArgumentResolvers(listOf(NotificationMessageArgumentResolver(messageConverter)))
return queueMessageHandlerFactory
}
private fun buildAmazonSNSAsync(): AmazonSNSAsync {
return AmazonSNSAsyncClientBuilder.standard()
.withEndpointConfiguration(AwsClientBuilder.EndpointConfiguration(endpoint, region))
.withCredentials(AWSStaticCredentialsProvider(BasicAWSCredentials(accessKey, secretKey)))
.build()
}
private fun buildAmazonSQSAsync(): AmazonSQSAsync {
return AmazonSQSAsyncClientBuilder.standard()
.withEndpointConfiguration(AwsClientBuilder.EndpointConfiguration(endpoint, region))
.withCredentials(AWSStaticCredentialsProvider(BasicAWSCredentials(accessKey, secretKey)))
.build()
}
}
but when i send an object with LocalDateTime, even with #JsonFormat it keeps sending wrong format:
data class Blob(
#JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'")
val timestamp: LocalDateTime
)
i made this simple dispatch function to send messages through SNS(topic) and connected a queue to receive the responses.
class Dispatcher(
private val private val notificationTemplate: NotificationMessagingTemplate
)
fun dispatch(blob: Blob) {
val header = mapOf("randon" to "message")
notificationTemplate.convertAndSend("topic", blob, header)
}
wanted result :
\"timestamp\"=\"2021-11-26T19:18:46.905505\"
current result :
"timestamp\":{\"nano\":913070000,\"year\":2021,\"monthValue\":11,\"dayOfMonth\":26,\"hour\":19,\"minute\":18,\"second\":46,\"month\":\"NOVEMBER\",\"dayOfWeek\":\"FRIDAY\",\"dayOfYear\":330,\"chronology\":{\"calendarType\":\"iso8601\",\"id\":\"ISO\"}}
i don't have any clue about what is happening.
PS. i tested SQS too, and the result is the same.
after searching inside awspring, i found that the SimpleMessageConverter is set by default, after some research, i found the right way to set the custom mapper at sendAndConvert() function. here is the configurtions:
#Bean
fun notificationMessageTemplate(objectMapper: ObjectMapper): NotificationMessagingTemplate {
// here we set the custom message converter
val messageConverter = MappingJackson2MessageConverter()
messageConverter.objectMapper = objectMapper
// as we want to send it as a Json, we need to set the serialization to String
messageConverter.serializedPayloadClass = String::class.java
val notificationMessageTemplate = NotificationMessagingTemplate(buildAmazonSNSAsync())
// here is where we set the proper message converter
notificationMessageTemplate.messageConverter = messageConverter
return notificationMessageTemplate
}
#Bean
fun queueMessagingTemplate(objectMapper: ObjectMapper): QueueMessagingTemplate {
val messageConverter = MappingJackson2MessageConverter()
messageConverter.objectMapper = objectMapper
messageConverter.serializedPayloadClass = String::class.java
val queueMessageConverter = QueueMessagingTemplate(buildAmazonSQSAsync())
queueMessageConverter.messageConverter = messageConverter
return queueMessageConverter
}
after setting this mappers, every notation will be working fine.
here is the result:
\"timestamp\":\"2021-11-29T17:46:52Z\"

How to inject spring resources into Apache Ignite classes?

I have a problem with injection spring beans into some ignite's classes. I'm trying to create this: Client -> Apache Ignite -> Spring-Data -> DataBase
Maybe it is wrong, i'm not sure.
So, at this moment my classes look like:
AppConfiguration
#Configuration
#ComponentScan(basePackages = arrayOf("com.ignite.cache"))
open class AppConfiguration : Serializable {
private val logger: Logger = Logger.getLogger(AppConfiguration::class.java)
#Bean
open fun igniteInstance(): Ignite {
val cfg = IgniteConfiguration()
cfg.igniteInstanceName = "springDataNode"
cfg.isPeerClassLoadingEnabled = true
var clientCache: CacheConfiguration<Long, Client> = CacheConfiguration("ClientCache")
clientCache.apply {
setIndexedTypes(Long::class.java, Client::class.java)
setCacheStoreFactory(FactoryBuilder.factoryOf(ClientStore::class.java))
isReadThrough = true
isWriteThrough = true
}
cfg.setCacheConfiguration(clientCache)
return Ignition.start(cfg)
}
}
DataSourceConfiguration:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = arrayOf("com.ignite.cache.model.repositories.springdatarepository"))
#EnableIgniteRepositories(basePackages = arrayOf("com.ignite.cache.model.repositories.igniterepository"))
#ComponentScan(basePackages = arrayOf("com.ignite.cache.model"))
open class DataSourceConfiguration : Serializable {
#Bean
open fun entityManagerFactory(): LocalContainerEntityManagerFactoryBean {
var entityManagerFactory: LocalContainerEntityManagerFactoryBean = LocalContainerEntityManagerFactoryBean()
entityManagerFactory.apply {
dataSource = dataSource()
setPackagesToScan("com.ignite.cache.model")
var vendorAdapter: HibernateJpaVendorAdapter = HibernateJpaVendorAdapter()
vendorAdapter.apply {
setGenerateDdl(true)
setShowSql(true)
}
var properties: Properties = Properties()
properties.apply {
put("database.dialet", "org.hibernate.dialect.PostgreSQL95Dialect")
put("database.globally_quoted_identifiers", "false")
put("database.enable_lazy_load_no_trans", "true")
put("database.show_sql", "true")
}
jpaVendorAdapter = vendorAdapter
setJpaProperties(properties)
}
return entityManagerFactory
}
#Bean
open fun dataSource(): DataSource {
var source: ComboPooledDataSource = ComboPooledDataSource()
source.apply {
driverClass = "org.postgresql.Driver"
jdbcUrl = "jdbc:postgresql://localhost:5432/ignite"
user = "postgres"
password = "1111"
acquireIncrement = 5
idleConnectionTestPeriod = 60
maxPoolSize = 20
minPoolSize = 10
initialPoolSize = 10
}
return source
}
#Bean
open fun transactionManager() : PlatformTransactionManager {
var manager: JpaTransactionManager = JpaTransactionManager()
manager.apply {
entityManagerFactory = entityManagerFactory().nativeEntityManagerFactory
}
return manager
}
#Bean
open fun exceptionTranslator(): PersistenceExceptionTranslationPostProcessor = PersistenceExceptionTranslationPostProcessor()
}
Entity:
#Entity
#Table(name = "client")
data class Client
(
#Id
#Column(name = "id")
#GeneratedValue(generator = "increment")
#GenericGenerator(name = "increment", strategy = "increment")
var id: Long = 0,
#Column(name = "email", nullable = false, unique = true)
var email: String = "",
#Column(name = "login", nullable = false, unique = true)
var login: String = ""
) : Serializable
Repositories:
#RepositoryConfig(cacheName = "ClientCache")
interface IgniteClientRepository : IgniteRepository<Client, Long> {
}
#Repository
interface ClientRepository : CrudRepository<Client, Long>
Standart implemenation for service classes and CacheStore for ignite:
public class ClientStore implements CacheStore<Long, Client>, Serializable {
private Logger logger = Logger.getLogger(ClientStore.class);
#SpringResource
private IClientService clientRepository; // <- error is here (NPE)
#Override
public void loadCache(IgniteBiInClosure<Long, Client> igniteBiInClosure, #Nullable Object... objects) throws CacheLoaderException {
Iterable<Client> clients = clientRepository.findAll();
for(Client client : clients) {
igniteBiInClosure.apply(client.getId(), client);
}
}
...
}
Main:
public class Main {
private static Logger logger = Logger.getLogger(Main.class);
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfiguration.class, DataSourceConfiguration.class);
Ignite ignite = context.getBean(Ignite.class);
(ignite.getOrCreateCache("ClientCache")).loadCache(null);
IgniteClientRepository clientRepository = context.getBean(IgniteClientRepository.class);
Iterable<Client> clients = clientRepository.findAll();
for(Client client : clients) {
logger.info(client);
}
logger.info("Finish");
}
}
But when i try to load some data from database into cache i get error NPE:
Exception in thread "main" javax.cache.integration.CacheLoaderException: java.lang.NullPointerException
at org.apache.ignite.internal.processors.cache.store.GridCacheStoreManagerAdapter.loadCache(GridCacheStoreManagerAdapter.java:528)
at org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheAdapter.localLoadCache(GridDhtCacheAdapter.java:486)
at org.apache.ignite.internal.processors.cache.GridCacheProxyImpl.localLoadCache(GridCacheProxyImpl.java:217)
at org.apache.ignite.internal.processors.cache.GridCacheAdapter$LoadCacheJob.localExecute(GridCacheAdapter.java:5439)
at org.apache.ignite.internal.processors.cache.GridCacheAdapter$LoadCacheJobV2.localExecute(GridCacheAdapter.java:5488)
at org.apache.ignite.internal.processors.cache.GridCacheAdapter$TopologyVersionAwareJob.execute(GridCacheAdapter.java:6103)
at org.apache.ignite.compute.ComputeJobAdapter.call(ComputeJobAdapter.java:132)
at org.apache.ignite.internal.processors.closure.GridClosureProcessor$C2.execute(GridClosureProcessor.java:1842)
at org.apache.ignite.internal.processors.job.GridJobWorker$2.call(GridJobWorker.java:566)
at org.apache.ignite.internal.util.IgniteUtils.wrapThreadLoader(IgniteUtils.java:6621)
at org.apache.ignite.internal.processors.job.GridJobWorker.execute0(GridJobWorker.java:560)
at org.apache.ignite.internal.processors.job.GridJobWorker.body(GridJobWorker.java:489)
at org.apache.ignite.internal.util.worker.GridWorker.run(GridWorker.java:110)
at org.apache.ignite.internal.processors.job.GridJobProcessor.processJobExecuteRequest(GridJobProcessor.java:1114)
at org.apache.ignite.internal.processors.task.GridTaskWorker.sendRequest(GridTaskWorker.java:1379)
at org.apache.ignite.internal.processors.task.GridTaskWorker.processMappedJobs(GridTaskWorker.java:640)
at org.apache.ignite.internal.processors.task.GridTaskWorker.body(GridTaskWorker.java:532)
at org.apache.ignite.internal.util.worker.GridWorker.run(GridWorker.java:110)
at org.apache.ignite.internal.processors.task.GridTaskProcessor.startTask(GridTaskProcessor.java:743)
at org.apache.ignite.internal.processors.task.GridTaskProcessor.execute(GridTaskProcessor.java:443)
at org.apache.ignite.internal.processors.closure.GridClosureProcessor.callAsync(GridClosureProcessor.java:447)
at org.apache.ignite.internal.processors.closure.GridClosureProcessor.callAsync(GridClosureProcessor.java:418)
at org.apache.ignite.internal.processors.closure.GridClosureProcessor.callAsync(GridClosureProcessor.java:402)
at org.apache.ignite.internal.processors.cache.GridCacheAdapter.globalLoadCacheAsync(GridCacheAdapter.java:3681)
at org.apache.ignite.internal.processors.cache.GridCacheAdapter.globalLoadCache(GridCacheAdapter.java:3657)
at org.apache.ignite.internal.processors.cache.IgniteCacheProxy.loadCache(IgniteCacheProxy.java:387)
at com.ignite.cache.Main.main(Main.java:22)
Caused by: java.lang.NullPointerException
at com.ignite.cache.model.service.ClientStore.loadCache(ClientStore.java:30)
at org.apache.ignite.internal.processors.cache.store.GridCacheStoreManagerAdapter.loadCache(GridCacheStoreManagerAdapter.java:502)
... 26 more
I still can't figure out why my client service doesn't inject into CacheStore class. Maybe i should use xml config instead of java-class config for ignite?
When you start Ignite using Ignition#start with IgniteConfiguration object, it's not aware of Spring context at all. You need to use IgniteSpring#start methods instead to provide context explicitly. Another option is to utilize IgniteSpringBean that already implements ApplicationContextAware and starts Ignite instance properly.
Also note that you will need to provide either bean name or bean class as a parameter to #SpringResource annotation.
I don't know Ignite and I don't know the programming language you are using but it looks like Ignite and not Spring is creating your ClientStore instances. Therefore you need to inject the dependencies manually.
I'm not really familiar with the FactoryBuilder used there, so there might be nice solutions if you can use a constructor with arguments or even a lambda, but if that doesn' work, you can store a reference to the repository in static field and get it from there in the constructor of the ClientStore.

Resources