All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.sarl.sre.janus.network.services.HazelcastContextService.sarl Maven / Gradle / Ivy

The newest version!
/*
 * $Id$
 *
 * SARL is an general-purpose agent programming language.
 * More details on http://www.sarl.io
 *
 * Copyright (C) 2014-2024 SARL.io, the Original Authors and Main Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.sarl.sre.janus.network.services

import com.hazelcast.core.EntryEvent
import com.hazelcast.core.EntryListener
import com.hazelcast.core.HazelcastInstance
import com.hazelcast.map.IMap
import com.hazelcast.map.MapEvent
import io.bootique.di.Injector
import io.sarl.lang.core.Agent
import io.sarl.sre.janus.KernelScope
import io.sarl.sre.janus.boot.configs.SreConfig
import io.sarl.sre.janus.internal.Factories
import io.sarl.sre.janus.services.context.Context
import io.sarl.sre.janus.services.context.ContextFactory
import io.sarl.sre.janus.services.context.MemoryBasedContextService
import io.sarl.sre.janus.services.logging.LoggingService
import java.text.MessageFormat
import java.util.UUID
import java.util.logging.Logger
import javax.inject.Inject
import javax.inject.Provider
import org.eclipse.xtend.lib.annotations.Accessors

/** Implementation of a context-space service that is connected to remote SRE with the Hazelcast framework.
 *
 * @author Nicolas Gaud
 * @version janus.network 3.0.14.0 20241106-161408
 * @mavengroupid io.sarl.sre.janus
 * @mavenartifactid janus.network
 * @since 0.12
 */
class HazelcastContextService extends MemoryBasedContextService {

	/** 
	 * Local Hazelcast instance
	 */
	@Accessors(PUBLIC_GETTER)
	var hazelcastInstance : HazelcastInstance;

	/** 
	 * Map linking a context id to its associated default space id. This map must be
	 * distributed and synchronized all over the network
	 */
	var defaultSpaces : IMap

	/** 
	 * ID of the listener defined on defaultSpaces map
	 */
	var defaultSpacesListenerID : UUID

	@Inject
	new(sreConfig : SreConfig, @KernelScope rootContext : Context, logger : LoggingService,
		injector : Injector, factory : ContextFactory,
		factories : Provider,
		hazelcastInstance : HazelcastInstance) {
		super(rootContext, logger, injector, factory, factories);

		this.hazelcastInstance = hazelcastInstance

		/*kernelLogger.log(Level::INFO, "Creating HazelcastContextService")
		kernelLogger.log(Level::INFO, "RootID from BootCOnfig: " + sreConfig.boot.rootContextID)
		kernelLogger.log(Level::INFO, "RootSpaceID from BootCOnfig: " + sreConfig.boot.rootSpaceID)
		kernelLogger.log(Level::INFO, "RootID from rootContext: " + rootContext.ID)
		kernelLogger.log(Level::INFO, "RootSpaceID from rootContext: " + rootContext.defaultSpace.spaceID.ID)*/
		this.logger.info[Messages::HazelcastContextService_0]
		this.defaultSpaces = this.hazelcastInstance.getMap(sreConfig.boot.rootContextID.toString());

		// Can't access to that rootContext.defaultSpace 
		// registering root context, the root context is created differently than other contexts using a call to the ContextFactory directly
		this.logger.info[Messages::HazelcastContextService_1]
		this.defaultSpaces.putIfAbsent(sreConfig.boot.rootContextID, sreConfig.boot.rootSpaceID);

		this.logger.info[Messages::HazelcastContextService_2]

		var defaultSpacesListener : EntryListener = new DefaultSpacesMapListener(this)
		this.defaultSpacesListenerID = this.defaultSpaces.addEntryListener(defaultSpacesListener, true)
		this.logger.info[Messages::HazelcastContextService_3]
	}

	protected override createLogger(loggingService : LoggingService) : Logger {
		loggingService.getKernelModuleLogger(Messages::HazelcastContextService_9)
	}

	protected def newContextInstance(contextID : UUID, defaultSpaceID : UUID, owner : Agent) : Context {
		this.logger.info[MessageFormat::format(Messages::HazelcastContextService_4, contextID, defaultSpaceID)]
		this.defaultSpaces.putIfAbsent(contextID, defaultSpaceID);
		return super.newContextInstance(contextID, defaultSpaceID, owner)
	}

	override removeContext(contextID : UUID) : Context {
		this.defaultSpaces.remove(contextID);
		super.removeContext(contextID)
	}

	override onStart {
		super.onStart

	}

	protected def ensureDefaultSpaceDefinition(contextID : UUID, defaultSpaceID : UUID, owner : Agent) {
		this.logger.info[MessageFormat::format(Messages::HazelcastContextService_5, defaultSpaceID, contextID)]

		if (!this.contextInternalStructure.containsKey(contextID)) {
			// Assuming the first request we got, is the one corresponding to the default space
			super.createContext(contextID, defaultSpaceID, owner);
		} else {
			if (getContext(contextID).defaultSpace.spaceID.ID != defaultSpaceID) {
				this.logger.severe[MessageFormat::format(Messages::HazelcastContextService_6, defaultSpaceID, contextID)]
			}
		}
	}

	protected def removeDefaultSpaceDefinition(contextID : UUID, defaultSpaceID : UUID) {
		if (getContext(contextID).defaultSpace.isPseudoEmpty()) {

			if (getContext(contextID).defaultSpace.spaceID.ID === defaultSpaceID) {

				this.logger.info[MessageFormat::format(Messages::HazelcastContextService_7, defaultSpaceID, contextID)]
				super.removeContext(contextID)

			} else {
				this.logger.severe[MessageFormat::format(Messages::HazelcastContextService_8, defaultSpaceID, contextID)]
			}

		}
	}

	override onStop {
		super.onStop
		// TODO check if it is really necessary to clean this distributed map of Contexts, because it will impact other kernels staying active on the network
		this.defaultSpaces.clear
		this.defaultSpaces.removeEntryListener(this.defaultSpacesListenerID)

		if (hazelcastInstance.lifecycleService.running) {
			hazelcastInstance.shutdown();
		}
	}

	/** Listener on Hazelcast's shared map events.
	 *
	 * @author Nicolas Gaud
	 * @version janus.network 3.0.14.0 20241106-161408
	 * @mavengroupid io.sarl.sre.janus
	 * @mavenartifactid janus.network
	 * @since 0.12
	 */
	protected static class DefaultSpacesMapListener implements EntryListener {
		var hazelcastContextService : HazelcastContextService


		new(hazelcastContextService : HazelcastContextService) {
			this.hazelcastContextService = hazelcastContextService
		}

		def entryAdded(^event : EntryEvent) {
			this.hazelcastContextService.ensureDefaultSpaceDefinition(^event.key, ^event.value, null)
		}

		def entryUpdated(^event : EntryEvent) {
			//
		}

		def entryRemoved(^event : EntryEvent) {
			this.hazelcastContextService.removeDefaultSpaceDefinition(^event.key, ^event.value)
		}

		def entryEvicted(^event : EntryEvent) {
			//
		}

		def entryExpired(^event : EntryEvent) {
			//
		}

		def mapCleared(^event : MapEvent) {
			//
		}

		def mapEvicted(^event : MapEvent) {
			//
		}

	}

	

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy