redtank created IGNITE-6462: ------------------------------- Summary: @SpringResource block Service Node bootstrap when the node starts at the first time Key: IGNITE-6462 URL: https://issues.apache.org/jira/browse/IGNITE-6462 Project: Ignite Issue Type: Bug Components: managed services, spring Affects Versions: 2.1, 2.2 Environment: OS: OSX 10.12 Java: 1.8.0_112-b16 Kotlin: 1.1.4-3 Reporter: redtank
@SpringResource block Service Node bootstrap and service deployment when the node starts at the first time. After killing the service node and restarting, the service is deployed successfully. My steps is 1. Start the data node ``` fun main(args: Array<String>) { SpringApplication(DataNodeApplication::class.java).apply { addInitializers( ApplicationContextInitializer<GenericApplicationContext> { DataNodeApplication.beans().initialize(it) } ) }.run(*args) } @SpringBootConfiguration @EnableAutoConfiguration class DataNodeApplication { companion object { fun beans() = beans { bean("igniteInstance") { // Ignite configuration with all defaults // and enabled p2p deployment and enabled events. val igniteConfig = IgniteConfiguration().apply { isPeerClassLoadingEnabled = true /* Labeling Data Nodes with special attribute. This attribute is checked by common.filters.DataNodeFilters which decides where caches have to be deployed. */ userAttributes = mutableMapOf("data.node" to true) // Configuring caches that will be deployed on Data Nodes setCacheConfiguration( // Cache for QuoteRequest CacheConfiguration<Int, QuoteRequest>().apply { name = "QuoteRequest" /* Enabling a special nodes filter for the cache. The filter will make sure that the cache will be deployed only on Data Nodes, the nodes that have 'data.node' attribute in the local node map. */ nodeFilter = DataNodeFilter() }, // Cache for Maintenance records CacheConfiguration<Int, Maintenance>().apply { name = "maintenance" /* Enabling a special nodes filter for the cache. The filter will make sure that the cache will be deployed only on Data Nodes, the nodes that have 'data.node' attribute in the local node map. */ nodeFilter = DataNodeFilter() // Enabling our sample cache store for the Maintenance cache setCacheStoreFactory(FactoryBuilder.factoryOf("common.cachestore.SimpleCacheStore")) // Avoid Maintenance objects deserialization on data nodes side // when they are passed to SampleCacheStore. isStoreKeepBinary = true // Enabling the write-through feature for the store. isWriteThrough = true // Enabling the read-through feature for the store. isReadThrough = true // Configuring SQL schema. queryEntities = listOf( QueryEntity().apply { // Setting indexed type's key class keyType = "java.lang.Integer" // Setting indexed type's value class valueType = "entity.Maintenance" // Defining fields that will be either indexed or queryable. // Indexed fields are added to 'indexes' list below. fields = linkedMapOf("vehicleId" to "java.lang.Integer") // Defining indexed fields. // Single field (aka. column) index indexes = listOf(QueryIndex("vehicleId")) } ) } ) discoverySpi = discoverySpi() } IgniteSpringBean().apply { configuration = igniteConfig } } } } } ``` 2. Start the service node ``` fun main(args: Array<String>) { SpringApplication(RequestForQuoteServiceNode::class.java).apply { addInitializers( ApplicationContextInitializer<GenericApplicationContext> { RequestForQuoteServiceNode.beans().initialize(it) } ) }.run(*args) } @SpringBootConfiguration @EnableAutoConfiguration @EnableIgniteRepositories("repository") class RequestForQuoteServiceNode { companion object { fun beans() = beans { bean("igniteInstance") { // Ignite configuration with all defaults // and enabled p2p deployment and enabled events. val igniteConfig = IgniteConfiguration().apply { isPeerClassLoadingEnabled = true /* Labeling QuoteRequest Service nodes with special attribute. This attribute is checked by common.filters.VehicleServiceFilter. Due to the filter, the RequestForQuoteService might be deployed only on the nodes with this special attribute set. */ userAttributes = mutableMapOf("vehicle.service.node" to true) setServiceConfiguration( // Setting up RequestForQuoteService. // The service will be deployed automatically // according to the configuration below. ServiceConfiguration().apply { name = "RequestForQuoteService" // Don't initialize service's properties here. Ignite's deployment // will discard them. service = RequestForQuoteServiceImpl() // Only one instance of the service will be deployed cluster wide totalCount = 1 // Only one instance of the service can be deployed on a single node. maxPerNodeCount = 1 /* Enabling a special nodes filter for this service. The filter will make sure that the service will be deployed only on the nodes that have 'quoteRequest.service.node' attribute in the local node map. */ nodeFilter = VehicleServiceFilter() } ) discoverySpi = discoverySpi() } IgniteSpringBean().apply { configuration = igniteConfig } } } } } ``` class RequestForQuoteServiceImpl : RequestForQuoteService { @Transient @SpringResource(resourceClass = QuoteRequestRepository::class) lateinit var quoteRequestRepository: QuoteRequestRepository @IgniteInstanceResource lateinit var ignite: Ignite /** Reference to the cache. */ lateinit private var quoteRequestCache: IgniteCache<Int, QuoteRequest> /** {@inheritDoc} */ override fun init(ctx: ServiceContext) { println("Initializing RequestForQuote Service on node:" + ignite.cluster().localNode()) /** * It's assumed that the cache has already been deployed. To do that, make sure to start Data Nodes with * a respective cache configuration. */ quoteRequestCache = ignite.cache("QuoteRequest") } /** {@inheritDoc} */ override fun execute(ctx: ServiceContext) { println("Executing QuoteRequest Service on node:" + ignite.cluster().localNode()) // Some custom logic. } /** {@inheritDoc} */ override fun cancel(ctx: ServiceContext) { println("Stopping QuoteRequest Service on node:" + ignite.cluster().localNode()) // Some custom logic. } /** {@inheritDoc} */ override fun addQuoteRequest(quoteRequestId: Int, quoteRequest: QuoteRequest) { // quoteRequestCache.put(quoteRequestId, quoteRequest) println("incoming Quote Request: id = $quoteRequestId, $quoteRequest") quoteRequestRepository.save(quoteRequestId, quoteRequest) } /** {@inheritDoc} */ override fun getQuoteRequest(quoteRequestId: Int): QuoteRequest { return quoteRequestCache.get(quoteRequestId) } /** {@inheritDoc} */ override fun removeQuoteRequest(quoteRequestId: Int) { quoteRequestCache.remove(quoteRequestId) } } ``` ``` interface RequestForQuoteService : Service { /** * Calls the service to add a new quoteRequest. * * @param quoteRequestId QuoteRequest unique ID. * @param quoteRequest QuoteRequest instance to add. */ fun addQuoteRequest(quoteRequestId: Int, quoteRequest: QuoteRequest) /** * Calls the service to get details for a specific QuoteRequest. * * @param quoteRequestId QuoteRequest unique ID. */ fun getQuoteRequest(quoteRequestId: Int): QuoteRequest /** * Calls the service to remove a specific vehicle. * * @param quoteRequestId QuoteRequest unique ID. */ fun removeQuoteRequest(quoteRequestId: Int) } ``` ``` public class VehicleServiceFilter implements IgnitePredicate<ClusterNode> { /** * Checks if {@code node} needs to be considered as a QuoteRequest Service Node. * * @param node Cluster node instance. * * @return {@code true} if the node has to be considered as QuoteRequest Service Node, {@code false} otherwise. */ public boolean apply(ClusterNode node) { Boolean dataNode = node.attribute("vehicle.service.node"); return dataNode != null && dataNode; } } ``` ``` @RepositoryConfig(cacheName = "QuoteRequest") interface QuoteRequestRepository : IgniteRepository<QuoteRequest, Int> ``` -- This message was sent by Atlassian JIRA (v6.4.14#64029)