From 41bb27d78bb031d3da68d31b6bd149b494538fff Mon Sep 17 00:00:00 2001 From: Nicolas Gaud Date: Wed, 15 Jan 2020 16:04:43 +0100 Subject: [PATCH] Adding janus v3 SARL Code --- .../sarl/revision/BehaviorGuardEvaluator.sarl | 103 +++ .../BehaviorGuardEvaluatorRegistry.sarl | 686 +++++++++++++++ .../src/io/sarl/revision/Messages.sarl | 47 + .../src/io/sarl/revision/messages.properties | 3 + .../src/io/sarl/sre/Kernel.sarl | 404 +++++++++ .../src/io/sarl/sre/Messages.sarl | 49 ++ .../src/io/sarl/sre/SreVersion.sarl | 47 + .../src/io/sarl/sre/boot/Boot.sarl | 71 ++ .../src/io/sarl/sre/boot/SreMain.sarl | 373 ++++++++ .../sre/boot/commands/AbstractRunCommand.sarl | 195 +++++ .../io/sarl/sre/boot/commands/Messages.sarl | 60 ++ .../boot/commands/RunSingleAgentCommand.sarl | 140 +++ .../boot/commands/RunWithoutAgentCommand.sarl | 136 +++ .../sre/boot/commands/VersionCommand.sarl | 128 +++ .../sre/boot/commands/messages.properties | 13 + .../io/sarl/sre/boot/configs/Messages.sarl | 50 ++ .../io/sarl/sre/boot/configs/SreConfig.sarl | 211 +++++ .../sarl/sre/boot/configs/messages.properties | 6 + .../boot/configs/subconfigs/BootConfig.sarl | 525 +++++++++++ .../configs/subconfigs/ExecutorsConfig.sarl | 327 +++++++ .../sre/boot/configs/subconfigs/Messages.sarl | 68 ++ .../configs/subconfigs/RootContextType.java | 107 +++ .../configs/subconfigs/ServicesConfig.sarl | 238 +++++ .../configs/subconfigs/messages.properties | 25 + .../internal/ListenerNotifierModule.sarl | 67 ++ .../boot/internal/internal/LockModule.sarl | 69 ++ .../sre/boot/internal/internal/Messages.sarl | 50 ++ .../internal/PlatformEventEmitterModule.sarl | 111 +++ .../internal/internal/messages.properties | 3 + .../boot/internal/kernel/KernelModule.sarl | 109 +++ .../sre/boot/internal/kernel/Messages.sarl | 49 ++ .../SarlSpecificationCheckerModule.sarl | 66 ++ .../boot/internal/kernel/messages.properties | 2 + .../sre/boot/internal/naming/Messages.sarl | 49 ++ .../internal/naming/NameParserModule.sarl | 68 ++ .../naming/SchemeNameParserModule.sarl | 99 +++ .../boot/internal/naming/messages.properties | 2 + .../internal/services/AllServicesModule.sarl | 41 + .../services/ContextServiceModule.sarl | 74 ++ .../services/ExecutorServiceModule.sarl | 203 +++++ .../services/InfrastructureServiceModule.sarl | 71 ++ .../services/LifecycleServiceModule.sarl | 72 ++ .../services/LoggerCreatorModule.sarl | 91 ++ .../services/LoggingServiceModule.sarl | 97 ++ .../sre/boot/internal/services/Messages.sarl | 57 ++ .../services/NamespaceFinderModule.sarl | 99 +++ .../services/NamespaceServiceModule.sarl | 73 ++ .../internal/services/ProbeServiceModule.sarl | 106 +++ .../internal/services/TimeServiceModule.sarl | 71 ++ .../internal/services/messages.properties | 10 + .../skills/BuiltinCapacityModule.sarl | 66 ++ .../boot/internal/skills/EventBusModule.sarl | 79 ++ .../sre/boot/internal/skills/Messages.sarl | 49 ++ .../boot/internal/skills/messages.properties | 2 + .../sre/boot/internal/spaces/Messages.sarl | 48 + .../boot/internal/spaces/SpacesModule.sarl | 82 ++ .../boot/internal/spaces/messages.properties | 1 + .../sre/capacities/InformedEventListener.sarl | 57 ++ .../sarl/sre/capacities/InternalEventBus.sarl | 156 ++++ .../sre/capacities/InternalSchedules.sarl | 49 ++ .../io/sarl/sre/capacities/MicroKernel.sarl | 44 + .../sarl/sre/internal/ListenerNotifier.sarl | 79 ++ .../src/io/sarl/sre/internal/Messages.sarl | 59 ++ .../sre/internal/PlatformEventEmitters.sarl | 377 ++++++++ .../sre/internal/SmartListenerCollection.sarl | 66 ++ .../io/sarl/sre/internal/messages.properties | 10 + .../src/io/sarl/sre/messages.properties | 4 + .../sre/naming/AbstractSchemeNameParser.sarl | 133 +++ .../src/io/sarl/sre/naming/AgentName.sarl | 61 ++ .../sre/naming/AgentSchemeNameParser.sarl | 76 ++ .../src/io/sarl/sre/naming/BehaviorName.sarl | 73 ++ .../sre/naming/BehaviorSchemeNameParser.sarl | 107 +++ .../src/io/sarl/sre/naming/ContextName.sarl | 51 ++ .../sre/naming/ContextSchemeNameParser.sarl | 65 ++ .../src/io/sarl/sre/naming/INameParser.sarl | 101 +++ .../io/sarl/sre/naming/ISchemeNameParser.sarl | 50 ++ .../src/io/sarl/sre/naming/NameParser.sarl | 106 +++ .../src/io/sarl/sre/naming/NameScheme.sarl | 34 + .../src/io/sarl/sre/naming/SarlName.sarl | 99 +++ .../src/io/sarl/sre/naming/ServiceName.sarl | 51 ++ .../sre/naming/ServiceSchemeNameParser.sarl | 65 ++ .../src/io/sarl/sre/naming/SkillName.sarl | 67 ++ .../sre/naming/SkillSchemeNameParser.sarl | 82 ++ .../src/io/sarl/sre/naming/SpaceName.sarl | 56 ++ .../sre/naming/SpaceSchemeNameParser.sarl | 68 ++ .../sre/services/AbstractServiceManager.sarl | 89 ++ .../sarl/sre/services/AbstractSreService.sarl | 73 ++ .../sre/services/GoogleServiceManager.sarl | 107 +++ .../io/sarl/sre/services/IServiceManager.sarl | 80 ++ .../src/io/sarl/sre/services/Messages.sarl | 61 ++ .../sre/services/PreReleasableService.sarl | 43 + .../context/AbstractContextService.sarl | 165 ++++ .../AbstractInjectionBasedContextService.sarl | 65 ++ .../io/sarl/sre/services/context/Context.sarl | 370 ++++++++ .../sre/services/context/ContextService.sarl | 184 ++++ .../context/LocalSpaceRepository.sarl | 90 ++ .../context/MemoryBasedContextService.sarl | 99 +++ .../sarl/sre/services/context/Messages.sarl | 47 + .../sre/services/context/SpaceRepository.sarl | 739 ++++++++++++++++ .../sre/services/context/messages.properties | 2 + .../executor/AbstractExecutorService.sarl | 239 +++++ .../services/executor/EarlyExitException.sarl | 60 ++ .../services/executor/ExecutorService.sarl | 260 ++++++ .../services/executor/JreExecutorService.sarl | 289 ++++++ .../sarl/sre/services/executor/Messages.sarl | 49 ++ .../sarl/sre/services/executor/Policies.sarl | 195 +++++ .../sarl/sre/services/executor/Runnables.sarl | 95 ++ .../services/executor/WrappedRunnables.sarl | 440 ++++++++++ .../sre/services/executor/messages.properties | 4 + .../BasicInfrastructureService.sarl | 39 + .../infrastructure/InfrastructureService.sarl | 43 + .../lifecycle/AbstractLifecycleService.sarl | 505 +++++++++++ .../lifecycle/AgentCreatorProvider.sarl | 52 ++ .../sre/services/lifecycle/AgentLife.sarl | 826 ++++++++++++++++++ .../sre/services/lifecycle/AgentState.java | 219 +++++ .../services/lifecycle/AgentTraitLife.sarl | 132 +++ .../sre/services/lifecycle/BehaviorLife.sarl | 79 ++ .../lifecycle/CannotSpawnException.sarl | 52 ++ .../InjectionBasedLifecycleService.sarl | 247 ++++++ .../services/lifecycle/LifecycleService.sarl | 275 ++++++ .../sarl/sre/services/lifecycle/Messages.sarl | 52 ++ .../services/lifecycle/SkillUninstaller.sarl | 126 +++ .../services/lifecycle/messages.properties | 7 + .../services/logging/JulLoggerCreator.sarl | 157 ++++ .../services/logging/JulLoggingService.sarl | 94 ++ .../sarl/sre/services/logging/JulStreams.sarl | 142 +++ .../sre/services/logging/LoggingService.sarl | 67 ++ .../services/logging/QuietLoggingService.sarl | 94 ++ .../io/sarl/sre/services/messages.properties | 12 + .../namespace/AbstractNamespaceFinder.sarl | 99 +++ .../namespace/AbstractNamespaceService.sarl | 94 ++ .../namespace/AgentNamespaceFinder.sarl | 62 ++ .../namespace/BehaviorNamespaceFinder.sarl | 83 ++ .../namespace/ContextNamespaceFinder.sarl | 67 ++ .../sre/services/namespace/FieldAccess.sarl | 100 +++ .../FinderBasedNamespaceService.sarl | 84 ++ .../services/namespace/INamespaceFinder.sarl | 51 ++ .../services/namespace/NamespaceService.sarl | 59 ++ .../namespace/ServiceNamespaceFinder.sarl | 72 ++ .../namespace/SkillNamespaceFinder.sarl | 81 ++ .../namespace/SpaceNamespaceFinder.sarl | 81 ++ .../services/probing/AbstractFieldProbe.sarl | 162 ++++ .../sre/services/probing/AbstractProbe.sarl | 261 ++++++ .../probing/AbstractProbeService.sarl | 219 +++++ .../probing/AsynchronousProbeService.sarl | 121 +++ .../sarl/sre/services/probing/FieldProbe.sarl | 200 +++++ .../sre/services/probing/IProbeListener.sarl | 42 + .../probing/IProbeReleaseListener.sarl | 42 + .../io/sarl/sre/services/probing/Probe.sarl | 109 +++ .../sre/services/probing/ProbeService.sarl | 88 ++ .../probing/SynchronousProbeService.sarl | 57 ++ .../services/time/AbstractTimeService.sarl | 84 ++ .../sre/services/time/JreTimeService.sarl | 79 ++ .../sarl/sre/services/time/TimeService.sarl | 157 ++++ .../src/io/sarl/sre/skills/BuiltinSkill.sarl | 46 + .../sre/skills/SreDynamicSkillProvider.sarl | 151 ++++ .../sarl/sre/skills/bic/BehaviorsSkill.sarl | 147 ++++ .../bic/DefaultContextInteractionsSkill.sarl | 118 +++ .../bic/ExternalContextAccessSkill.sarl | 246 ++++++ .../skills/bic/InnerContextAccessSkill.sarl | 191 ++++ .../sarl/sre/skills/bic/LifecycleSkill.sarl | 173 ++++ .../io/sarl/sre/skills/bic/LoggingSkill.sarl | 233 +++++ .../src/io/sarl/sre/skills/bic/Messages.sarl | 52 ++ .../sarl/sre/skills/bic/SchedulesSkill.sarl | 821 +++++++++++++++++ .../src/io/sarl/sre/skills/bic/TimeSkill.sarl | 75 ++ .../sarl/sre/skills/bic/messages.properties | 5 + .../io/sarl/sre/skills/internal/EventBus.sarl | 347 ++++++++ .../internal/InternalEventBusSkill.sarl | 336 +++++++ .../sre/skills/internal/MicroKernelSkill.sarl | 80 ++ .../sarl/sre/spaces/AbstractEventSpace.sarl | 267 ++++++ .../src/io/sarl/sre/spaces/AbstractSpace.sarl | 97 ++ .../io/sarl/sre/spaces/AddressLazyLinks.sarl | 70 ++ .../sre/spaces/EventTransportService.sarl | 77 ++ .../io/sarl/sre/spaces/LocalEventSpaces.sarl | 155 ++++ .../src/io/sarl/sre/spaces/Messages.sarl | 49 ++ .../src/io/sarl/sre/spaces/Participant.sarl | 94 ++ .../sre/spaces/SpaceParticipantListener.sarl | 50 ++ .../SpaceParticipantListenerFactory.sarl | 38 + .../sarl/sre/spaces/SpaceSpecifications.sarl | 182 ++++ .../sre/spaces/SpaceWithParticipants.sarl | 49 ++ .../io/sarl/sre/spaces/messages.properties | 2 + 181 files changed, 22164 insertions(+) create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/revision/BehaviorGuardEvaluator.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/revision/BehaviorGuardEvaluatorRegistry.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/revision/Messages.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/revision/messages.properties create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/Kernel.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/Messages.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/SreVersion.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/Boot.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/SreMain.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/commands/AbstractRunCommand.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/commands/Messages.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/commands/RunSingleAgentCommand.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/commands/RunWithoutAgentCommand.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/commands/VersionCommand.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/commands/messages.properties create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/Messages.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/SreConfig.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/messages.properties create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/subconfigs/BootConfig.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/subconfigs/ExecutorsConfig.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/subconfigs/Messages.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/subconfigs/RootContextType.java create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/subconfigs/ServicesConfig.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/subconfigs/messages.properties create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/internal/ListenerNotifierModule.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/internal/LockModule.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/internal/Messages.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/internal/PlatformEventEmitterModule.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/internal/messages.properties create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/kernel/KernelModule.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/kernel/Messages.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/kernel/SarlSpecificationCheckerModule.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/kernel/messages.properties create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/naming/Messages.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/naming/NameParserModule.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/naming/SchemeNameParserModule.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/naming/messages.properties create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/AllServicesModule.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/ContextServiceModule.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/ExecutorServiceModule.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/InfrastructureServiceModule.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/LifecycleServiceModule.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/LoggerCreatorModule.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/LoggingServiceModule.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/Messages.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/NamespaceFinderModule.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/NamespaceServiceModule.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/ProbeServiceModule.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/TimeServiceModule.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/messages.properties create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/skills/BuiltinCapacityModule.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/skills/EventBusModule.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/skills/Messages.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/skills/messages.properties create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/spaces/Messages.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/spaces/SpacesModule.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/spaces/messages.properties create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/capacities/InformedEventListener.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/capacities/InternalEventBus.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/capacities/InternalSchedules.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/capacities/MicroKernel.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/internal/ListenerNotifier.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/internal/Messages.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/internal/PlatformEventEmitters.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/internal/SmartListenerCollection.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/internal/messages.properties create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/messages.properties create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/AbstractSchemeNameParser.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/AgentName.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/AgentSchemeNameParser.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/BehaviorName.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/BehaviorSchemeNameParser.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/ContextName.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/ContextSchemeNameParser.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/INameParser.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/ISchemeNameParser.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/NameParser.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/NameScheme.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/SarlName.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/ServiceName.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/ServiceSchemeNameParser.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/SkillName.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/SkillSchemeNameParser.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/SpaceName.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/SpaceSchemeNameParser.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/AbstractServiceManager.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/AbstractSreService.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/GoogleServiceManager.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/IServiceManager.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/Messages.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/PreReleasableService.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/AbstractContextService.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/AbstractInjectionBasedContextService.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/Context.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/ContextService.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/LocalSpaceRepository.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/MemoryBasedContextService.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/Messages.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/SpaceRepository.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/messages.properties create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/AbstractExecutorService.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/EarlyExitException.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/ExecutorService.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/JreExecutorService.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/Messages.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/Policies.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/Runnables.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/WrappedRunnables.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/messages.properties create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/infrastructure/BasicInfrastructureService.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/infrastructure/InfrastructureService.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/AbstractLifecycleService.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/AgentCreatorProvider.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/AgentLife.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/AgentState.java create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/AgentTraitLife.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/BehaviorLife.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/CannotSpawnException.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/InjectionBasedLifecycleService.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/LifecycleService.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/Messages.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/SkillUninstaller.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/messages.properties create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/logging/JulLoggerCreator.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/logging/JulLoggingService.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/logging/JulStreams.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/logging/LoggingService.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/logging/QuietLoggingService.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/messages.properties create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/AbstractNamespaceFinder.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/AbstractNamespaceService.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/AgentNamespaceFinder.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/BehaviorNamespaceFinder.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/ContextNamespaceFinder.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/FieldAccess.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/FinderBasedNamespaceService.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/INamespaceFinder.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/NamespaceService.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/ServiceNamespaceFinder.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/SkillNamespaceFinder.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/SpaceNamespaceFinder.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/AbstractFieldProbe.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/AbstractProbe.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/AbstractProbeService.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/AsynchronousProbeService.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/FieldProbe.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/IProbeListener.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/IProbeReleaseListener.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/Probe.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/ProbeService.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/SynchronousProbeService.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/time/AbstractTimeService.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/time/JreTimeService.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/time/TimeService.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/BuiltinSkill.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/SreDynamicSkillProvider.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/BehaviorsSkill.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/DefaultContextInteractionsSkill.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/ExternalContextAccessSkill.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/InnerContextAccessSkill.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/LifecycleSkill.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/LoggingSkill.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/Messages.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/SchedulesSkill.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/TimeSkill.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/messages.properties create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/internal/EventBus.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/internal/InternalEventBusSkill.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/internal/MicroKernelSkill.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/AbstractEventSpace.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/AbstractSpace.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/AddressLazyLinks.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/EventTransportService.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/LocalEventSpaces.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/Messages.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/Participant.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/SpaceParticipantListener.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/SpaceParticipantListenerFactory.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/SpaceSpecifications.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/SpaceWithParticipants.sarl create mode 100644 sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/messages.properties diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/revision/BehaviorGuardEvaluator.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/revision/BehaviorGuardEvaluator.sarl new file mode 100644 index 0000000000..353127495c --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/revision/BehaviorGuardEvaluator.sarl @@ -0,0 +1,103 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.revision + +import com.google.common.base.Strings +import java.lang.reflect.Method +import java.text.MessageFormat +import java.util.Collection + +/** + * Describes each class having one of its methods annotated with {@code PerceptGuardEvaluator} annotation corresponding to the + * method in charge of evaluating the guard associated to a given event and returns the list of behaviors runnable that must be + * executed according to the result of the guard evaluation. + * @author $Author: ngaud$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * + */ +class BehaviorGuardEvaluator { + + /** The object with the {@code PerceptGuardEvaluator} method. */ + val target : Object + + /** {@code PerceptGuardEvaluator} method. */ + val method : Method + + /** Creates a {@code Subscriber} for {@code method} on {@code listener}. + * + * @param target the listener + * @param method the method to call to evaluate a guard + */ + new (target : Object, method : Method) { + this.target = target + this.method = method + } + + /** + * Evaluates the guard associated to the specified {@code event} and returns the list of behaviors methods that must be + * executed. + * @param event the event triggering behaviors + * @param behaviorsMethodsToExecute the list of behavior methods that will be completed according to the result of the guard + * evaluation, BE CARFEUL: we suppose that these behavior methods are parts of the SAME object where the + * {@code PerceptGuardEvaluator} method is declared + * @throws InvocationTargetException - exception during evaluation, can find the method to invoke + */ + def evaluateGuard(^event : Object, behaviorsMethodsToExecute : Collection) : void { + try { + this.method.accessible = true + this.method.invoke(this.target, ^event, behaviorsMethodsToExecute) + } catch (e : IllegalArgumentException) { + throw new Error(MessageFormat::format(Messages::BehaviorGuardEvaluator_0, ^event), e) + } catch (e : IllegalAccessException) { + throw new Error(MessageFormat::format(Messages::BehaviorGuardEvaluator_1, ^event), e) + } + } + + /** + * Returns he object instance containing the {@code PerceptGuardEvaluator}. + * @return the object instance containing the {@code PerceptGuardEvaluator} + */ + def getTarget : Object { + this.target + } + + override hashCode : int { + (31 + this.method.hashCode) * 31 + System::identityHashCode(this.target) + } + + override equals(obj : Object) : boolean { + if (obj instanceof BehaviorGuardEvaluator) { + // Use == so that different equal instances will still receive events. + // We only guard against the case that the same object is registered + // multiple times + return this.target === obj.target && this.method == obj.method + } + return false + } + + override toString : String { + if (this.method === null) Strings::emptyToNull(null) else this.method.name + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/revision/BehaviorGuardEvaluatorRegistry.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/revision/BehaviorGuardEvaluatorRegistry.sarl new file mode 100644 index 0000000000..750aff479e --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/revision/BehaviorGuardEvaluatorRegistry.sarl @@ -0,0 +1,686 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.revision + +import com.google.common.base.Objects +import com.google.common.cache.CacheBuilder +import com.google.common.cache.LoadingCache +import com.google.common.collect.Collections2 +import com.google.common.collect.ImmutableMap +import com.google.common.collect.ImmutableSet +import com.google.common.collect.Maps +import com.google.common.reflect.TypeToken +import io.sarl.lang.annotation.PerceptGuardEvaluator +import io.sarl.lang.core.Event +import io.sarl.lang.util.OutParameter +import java.lang.reflect.Method +import java.util.Arrays +import java.util.Collection +import java.util.Iterator +import java.util.List +import java.util.Map +import java.util.Map.Entry +import java.util.Set +import java.util.TreeMap +import java.util.TreeSet +import java.util.concurrent.CopyOnWriteArraySet + +/** + * Registry of all {@code BehaviorGuardEvaluator} classes containing a method to evaluate the guard of a given behavior (on clause in SARL behavior). + * This class has been inspired by the {@code com.google.common.eventbus.SuscriberRegistry} class of Google Guava library. + * + *

This class is not thread-safe. + * + * @author $Author: ngaud$ + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * + */ +class BehaviorGuardEvaluatorRegistry { + + private static def reloadClass(context : Class, type : Class) : Class { + var ld = context.classLoader + if (ld === null) { + ld = type.classLoader + } + try { + return ld.loadClass(type.name) + } catch (ex : Throwable) { + return type + } + } + + private static def safeCast(obj : Object) : T with T { + obj as T + } + + private static def exploreTypeHierarchy(concreteClass : Class) : Set> { + TypeToken::of(concreteClass).types.rawTypes.safeCast + } + + private static def getTypeHierarchyOnDemand(concreteClass : Class) : ImmutableSet> { + assert concreteClass !== null + val typeHierarchy = concreteClass.exploreTypeHierarchy + try { + val eventType = concreteClass.reloadClass(typeof(Event)) + if (eventType.isAssignableFrom(concreteClass)) { + return ImmutableSet::copyOf(typeHierarchy.filter [ eventType.isAssignableFrom(it) ]) + } + } catch (ex : Exception) { + // + } + ImmutableSet::copyOf(typeHierarchy) + } + + private static def getAnnotatedMethodMapOnDemand(concreteClass : Class) : Map, Collection> { + assert concreteClass !== null + // TODO verify if it effectively explores the whole type hierarchy + val typeHierarchy = concreteClass.exploreTypeHierarchy + val supertypes = typeHierarchy.filter [ !it.isInterface && typeof(Object) != it ] + + val identifiers = new TreeMap + + // Traverse all methods of the whole inheritance hierarchy + for (supertype : supertypes) { + for (method : supertype.declaredMethods) { + if (method.isAnnotationPresent(typeof(PerceptGuardEvaluator)) && !method.synthetic) { + val parameterTypes = method.parameterTypes + val ident = new MethodIdentifier(method, parameterTypes) + identifiers.putIfAbsent(ident, method) + } + } + } + + val buffer : Map, Collection> = new TreeMap [ elt1, elt2 | elt1.name <=> elt2.name ] + for (method : identifiers.values) { + val parameterTypes = method.parameterTypes + // Check the prototype of the event handler in debug mode only + assert checkEventHandlerPrototype(parameterTypes) + val eventType = parameterTypes.get(0) as Class + var methods = buffer.get(eventType) + if (methods === null) { + methods = newArrayList + buffer.put(eventType, methods) + } + methods += method + } + + ImmutableMap::copyOf(buffer) + } + + private static def checkEventHandlerPrototype(parameterTypes : Class[]) : boolean { + try { + if (parameterTypes.length === 2 + && parameterTypes.get(0) !== null + && reloadClass(parameterTypes.get(0), typeof(Event)).isAssignableFrom(parameterTypes.get(0)) + && parameterTypes.get(1) !== null + && reloadClass(parameterTypes.get(1), typeof(Collection)).isAssignableFrom(parameterTypes.get(1))) { + return true + } + } catch (ex : Exception) { + // + } + return false + } + + private static def getAnnotatedMethodsPerEvent(listenerType : Class) : Map, Collection> { + PERCEPT_GUARD_EVALUATOR_METHOD_CACHE.getUnchecked(listenerType) + } + + /** + * Thread-safe cache of classes to their flattened hierarchy of supertypes. + */ + static val FLATTEN_HIERARCHY_CACHE + : LoadingCache, ImmutableSet>> + = CacheBuilder::newBuilder.weakKeys.build [ it.getTypeHierarchyOnDemand ] + + /** + * A thread-safe cache that contains the mapping from each class to all methods in that class and all super-classes, that are annotated with + * the annotation given by {@link #perceptGuardEvaluatorAnnotation}. The cache is shared across all instances of this class; this greatly + * improves performance if multiple EventBus instances are created and objects of the same class are registered on all of them. + */ + static val PERCEPT_GUARD_EVALUATOR_METHOD_CACHE + : LoadingCache, Map, Collection>> + = CacheBuilder::newBuilder.weakKeys.build [ it.getAnnotatedMethodMapOnDemand ] + + /** + * It stores the information related to a given method especially its prototype. + * + * @author $Author: ngaud$ + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * + */ + private static final class MethodIdentifier implements Comparable { + + /** + * the name of the considered method. + */ + val name : String + + /** + * The list of the type of the various parameters of the considered method. + */ + val parameterTypes : List> + + /** + * Creates a new method identifier according to the name and the list of parameter types of the considered method. + * + * @param method + * - the name of the considered method. + * @param parameterTypes + * - The list of the type of the various parameters of the considered method. + */ + new (method : Method, parameterTypes : Class[]) { + // Useful to maintain the getDeclaringClass to obtain method of all classes with the inheritance hierarchy + this.name = method.declaringClass + method.name + this.parameterTypes = Arrays::asList(parameterTypes) + } + + override hashCode : int { + Objects::hashCode(this.name, this.parameterTypes) + } + + override equals(object : Object) : boolean { + if (object instanceof MethodIdentifier) { + return this.name == object.name && this.parameterTypes == object.parameterTypes + } + return false + } + + override compareTo(obj : MethodIdentifier) : int { + if (obj === null) { + return -1 + } + var cmp = this.name <=> obj.name + if (cmp !== 0) { + return cmp + } + cmp = this.parameterTypes.size <=> obj.parameterTypes.size + if (cmp !== 0) { + return cmp + } + val it1 = this.parameterTypes.iterator + val it2 = obj.parameterTypes.iterator + while (it1.hasNext) { + assert it2.hasNext + cmp = it1.next.name <=> it2.next.name + if (cmp !== 0) { + return cmp + } + } + return 0 + } + + override toString : String { + this.name + } + + } + + /** Iterator on guard evaluators. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ + private static class EvaluatorIterator implements Iterator, Collection>> { + + val listener : Object + + val iterator : Iterator, Collection>> + + new (listener : Object) { + val methods = getAnnotatedMethodsPerEvent(listener.class) + this.iterator = methods.entrySet.iterator + this.listener = listener + } + + override hasNext : boolean { + this.iterator.hasNext + } + + override next : Pair, Collection> { + val entry = this.iterator.next + new Pair(entry.key, Collections2::transform(entry.value) + [ new BehaviorGuardEvaluator(this.listener, it) ]) + } + + } + + /** Iterator on behavior guard evaluators. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.5.0 + */ + private static class EvaluatorCollectionIterator implements Iterator { + + val behaviorGuardEvaluators : Map, Pair<(Event) => Boolean, Set>> + + val ^event : Event + + val eventTypeIterator : Iterator> + + val skipSubscriberFiltering : boolean + + var evaluators : Iterator + + new (behaviorGuardEvaluators : Map, Pair<(Event) => Boolean, Set>>, + ^event : Event, + eventTypes : Iterator>, + skipSubscriberFiltering : boolean) { + assert behaviorGuardEvaluators !== null + assert ^event !== null + this.skipSubscriberFiltering = skipSubscriberFiltering + this.behaviorGuardEvaluators = behaviorGuardEvaluators + this.^event = ^event + this.eventTypeIterator = eventTypes + searchNext + } + + private def searchNext : void { + while ((this.evaluators === null || !this.evaluators.hasNext) && this.eventTypeIterator.hasNext) { + val eventType = this.eventTypeIterator.next + val eventSubscribers = this.behaviorGuardEvaluators.get(eventType) + if (eventSubscribers.isValidSubscriber) { + this.evaluators = eventSubscribers.value.iterator + } + } + } + + protected def isValidSubscriber(subscriber : Pair<(Event) => Boolean, Set>) : boolean { + if (subscriber !== null && subscriber.value !== null) { + return this.skipSubscriberFiltering || subscriber.key === null || subscriber.key.apply(this.^event) + } + return false + } + + override hasNext : boolean { + this.evaluators !== null && this.evaluators.hasNext + } + + override next : BehaviorGuardEvaluator { + assert this.evaluators !== null + val next = this.evaluators.next + assert next !== null + searchNext + return next + } + + } + + /** Iterator on behavior guard evaluators. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.5.0 + */ + private static class EvaluatorCollectionFilteringIterator implements Iterator { + + val iterator : Iterator + + val subscriber : Object + + var next : BehaviorGuardEvaluator + + new (iterator : Iterator, subscriber : Object) { + assert iterator !== null + assert subscriber !== null + this.iterator = iterator + this.subscriber = subscriber + searchNext + } + + private def searchNext { + this.next = null + while (this.next === null && this.iterator.hasNext) { + val evaluator = this.iterator.next + if (evaluator.target === this.subscriber) { + this.next = evaluator + } + } + } + + override hasNext : boolean { + this.next !== null + } + + override next : BehaviorGuardEvaluator { + assert this.next !== null + val next = this.next + searchNext + return next + } + + } + + /** + * All registered {@code BehaviorGuardEvaluator}s (class containing at least one PerceptGuardEvaluator method), indexed by event type. + * + *

The {@link CopyOnWriteArraySet} values make it easy and relatively lightweight to get an immutable snapshot of all current + * {@code BehaviorGuardEvaluator}s to an event without any locking. + */ + val behaviorGuardEvaluators : Map, Pair<(Event) => Boolean, Set>> + + var active = true + + /** + * Instanciates a new registry linked with the {@link PerceptGuardEvaluator} annotation. + * + *

The registry will use concurrent data structures. + */ + new { + this(true) + } + + /** + * Instanciates a new registry linked with the {@link PerceptGuardEvaluator} annotation. + * + * @param concurrent indicates if the internal data structures must support thread concurrency, or not. + */ + new (concurrent : boolean) { + this(if (concurrent) Maps::newConcurrentMap else Maps::newHashMap) + } + + /** + * Instanciates a new registry linked with the {@link PerceptGuardEvaluator} annotation. + * + * @param buffer the buffer to be used for storing the behavior guard evaluators. + */ + new (buffer : Map, Pair<(Event) => Boolean, Set>>) { + assert buffer !== null + this.behaviorGuardEvaluators = buffer + } + + /** + * Registers all {@code PerceptGuardEvaluator} methods on the given listener object. + * + *

If the filter is provided, it will be used for determining if the given behavior accepts a specific event. + * If the filter function replies {@code true} for a specific event as argument, the event is fired in the + * behavior context. If the filter function replies {@code false}, the event is not fired in the behavior context. + * + * @param listener the new {@code BehaviorGuardEvaluator} to add + * @param filter the filter function. + * @param callback function which is invoked just after the first registration of the object. It could be {@code null}. + * @since 0.5.0 + */ + def register(listener : Object, filter : (Event) => Boolean = null, callback : (Object) => void = null) { + assert listener !== null + if (this.active) { + var firstInit = false + val listenerMethods = new EvaluatorIterator(listener) + while (listenerMethods.hasNext) { + val entry = listenerMethods.next + val eventType = entry.key + val eventMethodsInListener = entry.value + + val pair = this.behaviorGuardEvaluators.get(eventType) + var eventSubscribers : Set + + if (pair === null) { + eventSubscribers = new CopyOnWriteArraySet + this.behaviorGuardEvaluators.put(eventType, new Pair(filter, eventSubscribers)) + firstInit = true + } else { + eventSubscribers = pair.value + } + + if (eventSubscribers.addAll(eventMethodsInListener)) { + firstInit = true + } + } + if (firstInit && callback !== null) { + callback.apply(listener) + } + } + } + + /** + * Unregisters all BehaviorGuardEvaluators on all the listener objects. + * + * @param callback function which is invoked just before the object is unregistered. It could be {@code null}. + * @since 0.5.0 + */ + def unregisterAll(callback : (Object) => void = null) : void { + if (this.active) { + this.active = false + try { + if (callback !== null) { + val iterator = this.behaviorGuardEvaluators.values.iterator + val subscribers = new TreeSet [term1, term2 | + if (term1 === term2) { + return 0 + } + return System::identityHashCode(term1) <=> System::identityHashCode(term2) + ] + while (iterator.hasNext) { + val pair = iterator.next + for (evaluator : pair.value) { + if (subscribers += evaluator.target) { + callback.apply(evaluator.target) + } + } + iterator.remove + } + } else { + this.behaviorGuardEvaluators.clear + } + } finally { + this.active = true + } + } + } + + /** + * Unregisters all BehaviorGuardEvaluators on the given listener object. + * + * @param listener the {@code BehaviorGuardEvaluator} to remove + * @param callback function which is invoked just before the object is unregistered. It could be {@code null}. + * @since 0.5.0 + */ + def unregister(listener : Object, callback : (Object) => void = null) { + if (this.active) { + // TODO EvaluatorIterator is not efficient + val listenerMethods = new EvaluatorIterator(listener) + var listenerCallback = new OutParameter(callback) + while (listenerMethods.hasNext) { + val entry = listenerMethods.next + val eventType = entry.key + + val pair = this.behaviorGuardEvaluators.get(eventType) + + if (pair !== null && pair.value !== null) { + var candidates = pair.value + candidates.removeIf [ + if (it.target === listener) { + var cb = listenerCallback.get + if (cb !== null) { + cb.apply(listener) + listenerCallback.set(null) + } + return true + } + return false + ] + } + } + } + } + /** + * Unregisters all BehaviorGuardEvaluators on the listener objects of the givne type. + * + * @param listenerType the type of the {@code BehaviorGuardEvaluator} to remove + * @param callback function which is invoked just before the object is unregistered. It could be {@code null}. + * @since 0.7 + */ + def unregister(listenerType : Class, callback : (Object)=>void = null) { + if (this.active) { + var listenerCallback = callback + val listeners = new TreeSet [a, b | Integer::compare(System::identityHashCode(a), System::identityHashCode(b))] + for (handler : this.behaviorGuardEvaluators.entrySet) { + var pair = handler.value + if (pair !== null) { + var candidates = pair.value + if (candidates !== null) { + candidates.removeIf [ + var tgt = it.target + if (tgt !== null && listenerType.isInstance(tgt)) { + if (listenerCallback !== null && listeners.add(tgt)) { + listenerCallback.apply(tgt) + } + return true + } + return false + ] + } + } + } + } + } + + /** + * Gets an iterator representing an immutable snapshot of all BehaviorGuardEvaluators to the given event at the time this method is called. + * + * @param event + * -the event to process + * @return the set of guard evaluators associated to the specified event + */ + def getBehaviorGuardEvaluators(^event : Event) : Iterable { + val eventTypes = flattenHierarchy(^event.class) + new EvaluatorCollectionIterator(this.behaviorGuardEvaluators, ^event, eventTypes.iterator, false).toIterable + } + + /** + * Gets an iterator representing an immutable snapshot of all BehaviorGuardEvaluators of the given listener + * to the given event at the time this method is called. + * + *

Caution: This function does not apply filtering function given to {@link #register(Object, Function1, Procedure1)}. + * + * @param event -the event to process + * @param listener the owner of the BehaviorGuardEvaluators (never {@code null}). + * @return the set of guard evaluators associated to the specified event + * @since 0.5.0 + */ + def getBehaviorGuardEvaluatorsFor(^event : Event, listener : Object) : Iterable { + val eventTypes = flattenHierarchy(^event.class) + return [ + val base = new EvaluatorCollectionIterator(this.behaviorGuardEvaluators, ^event, eventTypes.iterator, true) + new EvaluatorCollectionFilteringIterator(base, listener) + ] + } + + /** Replies if a listener with the given type is registered. + * + * @param type the type of listener. + * @return {@code true} if a listener of the given type is registered. + * @since 0.5.0 + */ + def hasRegisteredEventListener(type : Class) : boolean { + // TODO: Not efficient implementation + for (pair : this.behaviorGuardEvaluators.values) { + val listeners = pair.value + if (listeners !== null) { + for (evaluator : listeners) { + val target = evaluator.target + if (type.isInstance(target)) { + return true + } + } + } + } + return false + } + + /** Extract the registered listeners with the given type. + * + * @param the type of the listeners. + * @param type the type of the listeners. + * @param collection the collection of listeners that is filled by this function. This argument could be {@code null}. + * @return the number of listeners of the given type. + * @since 0.5.0 + */ + def getRegisteredEventListeners(type : Class, collection : Collection) : int with T { + // TODO: Not efficient implementation + val addedObjects = new TreeSet [ elt1, elt2 | + if (elt1 === elt2) { + return 0 + } + System::identityHashCode(elt1) <=> System::identityHashCode(elt2) + ] + for (pair : this.behaviorGuardEvaluators.values) { + val listeners = pair.value + if (listeners !== null) { + for (evaluator : listeners) { + val target = evaluator.target + if (type.isInstance(target) && addedObjects.add(target)) { + if (collection !== null) { + collection += type.cast(target) + } + } + } + } + } + addedObjects.size + } + + /** Extract the registered listeners with the given type. + * + * @param the type of the listeners. + * @param type the type of the listeners. + * @return the listeners. + * @since 0.6.0 + */ + def getRegisteredEventListeners(type : Class) : Iterable with T { + val addedObjects = new TreeSet [ elt1, elt2 | + if (elt1 === elt2) { + return 0 + } + System::identityHashCode(elt1) <=> System::identityHashCode(elt2) + ] + return this.behaviorGuardEvaluators.values.map[it.value].flatten.map[it.target].filter [ + type.isInstance(it) && addedObjects.add(it) + ].map[type.cast(it)] + } + + /** + * Flattens a class's type hierarchy into a set of {@code Class} objects including all super-classes (transitively) and all interfaces implemented + * by these super-classes. + * + * @param concreteClass + * - the class you find the hierarchy + * @return the set of class in the hierarchy of the specififed class + */ + private static def flattenHierarchy(concreteClass : Class) : ImmutableSet> { + FLATTEN_HIERARCHY_CACHE.getUnchecked(concreteClass) + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/revision/Messages.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/revision/Messages.sarl new file mode 100644 index 0000000000..ae0efc11d8 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/revision/Messages.sarl @@ -0,0 +1,47 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.revision + +import org.eclipse.osgi.util.NLS + +/** Messages. + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +final class Messages extends NLS { + + static val BUNDLE_NAME = typeof(Messages).package.name + ".messages" + + public static var BehaviorGuardEvaluator_0 : String + public static var BehaviorGuardEvaluator_1 : String + public static var BehaviorGuardEvaluatorRegistry_0 : String + + static new { + NLS::initializeMessages(BUNDLE_NAME, typeof(Messages)) + } + + private new { + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/revision/messages.properties b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/revision/messages.properties new file mode 100644 index 0000000000..b65da4047f --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/revision/messages.properties @@ -0,0 +1,3 @@ +BehaviorGuardEvaluator_0=Invalid argument for the perception guard evaluator method becames inaccessible: {0} +BehaviorGuardEvaluator_1=Perception guard evaluator method becames inaccessible: {0} +BehaviorGuardEvaluatorRegistry_0=Method {0} has @{1} annotation but with invalid parameters. Perception guard evaluator methods must have exactly 2 parameters: the event occurence to dispatch, a subtype of Event; and the collection of BehaviorUnit methods to execute, Collection. The declared parameters are: {2}. diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/Kernel.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/Kernel.sarl new file mode 100644 index 0000000000..c3bc7e5dbb --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/Kernel.sarl @@ -0,0 +1,404 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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 + +import com.google.common.util.concurrent.Service +import com.google.inject.BindingAnnotation +import io.sarl.lang.annotation.PrivateAPI +import io.sarl.lang.core.Agent +import io.sarl.lang.core.AgentContext +import io.sarl.sre.capacities.InformedEventListener +import io.sarl.sre.services.IServiceManager +import io.sarl.sre.services.context.ContextService +import io.sarl.sre.services.lifecycle.KernelAgentLifecycleListener +import io.sarl.sre.services.lifecycle.LifecycleService +import io.sarl.sre.services.logging.LoggingService +import io.sarl.sre.spaces.AbstractEventSpace +import java.lang.Thread.UncaughtExceptionHandler +import java.lang.annotation.Retention +import java.lang.annotation.Target +import java.lang.ref.WeakReference +import java.util.List +import java.util.UUID +import java.util.concurrent.Executors +import java.util.concurrent.atomic.AtomicBoolean +import java.util.logging.Level +import java.util.logging.LogRecord +import java.util.logging.Logger +import javax.inject.Inject +import javax.inject.Singleton + +/** + * This class represents the Kernel of the SRE platform. + * + *

The Kernel is a singleton. + * + *

The Kernel is assimilated to an agent that is omniscient and distributed other the network. It is containing all the other + * agents. + * + *

To create a Kernel, you should use the function {@link #create(Module...)}. + * + * @author $Author: srodriguez$ + * @author $Author: ngaud$ + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +@Singleton +class Kernel { + + // value is true because the SRE kernel is running as soon as it is created. + val isRunning = new AtomicBoolean(true) + + val stopListener : AsynchronousKernelStoppingListener + + val serviceManager : IServiceManager + + val loggingService : LoggingService + + val spawnService : LifecycleService + + val contextService : ContextService + + /** + * Constructs a SRE kernel. + * + * @param serviceManager is the instance of the service manager that must be used by the kernel. + * The service manager must already have registered services inside itself. + * @param exceptionHandler is the handler of the uncaught exceptions. + */ + @SuppressWarnings("discouraged_reference") + @Inject + new (serviceManager : IServiceManager, exceptionHandler : UncaughtExceptionHandler) { + // Initialize the fields + this.stopListener = createKernelListener + this.serviceManager = serviceManager + this.loggingService = this.serviceManager.getService(typeof(LoggingService)) + this.spawnService = this.serviceManager.getService(typeof(LifecycleService)) + this.contextService = this.serviceManager.getService(typeof(ContextService)) + + // Ensure that all the threads has a default hander. + if (exceptionHandler !== null) { + Thread::setDefaultUncaughtExceptionHandler(exceptionHandler) + } + + // Listen on the kernel's events + this.spawnService.addKernelAgentLifecycleListener(this.stopListener) + + // Start the services NOW to ensure that the default context and space + // of the SRE agent are catched by the modules; + this.serviceManager.startServices(getLogger) + + // TODO Remove the following line when the framework is ready for the release + this.logger.warning(Messages::Kernel_3) + } + + /** Create an instance of the kernel event listener. + * + * @return the listener instance. + * @since 0.8.0 + */ + protected def createKernelListener : AsynchronousKernelStoppingListener { + new AsynchronousKernelStoppingListener(this) + } + + /** + * Replies if the kernel is running or not. + * + *

The kernel is running if at least one agent is alive. + * + * @return true if the kernel is running; false otherwise. + */ + def isRunning : boolean { + this.isRunning.get + } + + /** + * Replies the logger used by the kernel. + * + *

This function replies the logger of the logging service. If this service is down, + * {@code null} is replied. + * + * @return the logger of the kernel. + * @see #getShutdownSafeLogger() + */ + def getLogger : Logger { + this.loggingService.kernelLogger + } + + /** + * Spawn an agent of the given type, and pass the parameters to its initialization function. + * + * @param agent the type of the agent to spawn. + * @param callback the function invoked each time an agent is spawned. + * @param params the list of the parameters to pass to the agent initialization function. + * @return the identifier of the agent, never null. + */ + def spawn(^agent : Class, callback : (UUID) => void, arguments : Object*) { + val result = this.spawnService.spawnAgent(1, null, this.contextService.rootContext, null, ^agent, arguments) + if (result.failing) { + throw result.error + } + if (callback !== null && result.agentSpawned) { + var ags = result.spawnedAgents + if (!ags.empty) { + callback.apply(ags.head) + } + } + } + + /** + * Spawn agents of the given type, and pass the parameters to its initialization function. + * + * @param nbAgents the number of agents to spawn. + * @param agent the type of the agents to spawn. + * @param callback the function invoked each time an agent is spawned. + * @param params the list of the parameters to pass to the agent initialization function. + */ + def spawn(nbAgents : int, ^agent : Class, callback : (List)=>void, params : Object*) { + var result = this.spawnService.spawnAgent(nbAgents, null, this.contextService.rootContext, null, ^agent, params) + if (result.failing) { + var exception = result.error + this.loggingService.kernelLogger.log(Level::SEVERE, exception.localizedMessage, exception) + } + if (callback !== null) { + var ags = result.spawnedAgents + if (!ags.empty) { + callback.apply(ags) + } + } + } + + /** + * Spawn an agent of the given type, and pass the parameters to its initialization function. + * + * @param agentID the identifier of the agent to spawn. If null the identifier is randomly selected. + * @param agent the type of the agent to spawn. + * @param callback the function invoked each time an agent is spawned. + * @param params the list of the parameters to pass to the agent initialization function. + */ + def spawn(agentID : UUID, ^agent : Class, callback : (UUID)=>void, params : Object*) { + val result = this.spawnService.spawnAgent(1, null, this.contextService.rootContext, agentID, ^agent, params) + if (result.failing) { + throw result.error + } + if (callback !== null && result.agentSpawned) { + var ags = result.spawnedAgents + if (!ags.empty) { + callback.apply(ags.head) + } + } + } + + /** + * Replies a kernel service that is alive. + * + * @param - type of the type to reply. + * @param type type of the type to reply. + * @return the service, or null. + */ + def getService(type : Class) : S with S extends Service { + this.serviceManager.getService(type) + } + + /** + * Replies the SRE context of the kernel. + * + * @return the context for the kernel. + */ + def getRootContext : AgentContext { + this.contextService.rootContext + } + + /** Run the standard algorithm for stopping properly the kernel when no + * agent is alive. + * + * @param kern the kernel to stop. + */ + @SuppressWarnings("use_reserved_sarl_annotation") + @PrivateAPI + static def executeKernelStopWhenNoAgentAlive(kern : Kernel) { + val logger = kern.logger + logger.info(Messages.Kernel_0) + if (kern.stopListener !== null) { + kern.spawnService.removeKernelAgentLifecycleListener(kern.stopListener) + } + kern.serviceManager.stopServices(logger) + logger.info(Messages.Kernel_1) + kern.isRunning.set(false) + } + + /** Run the standard algorithm for stopping the agents on the kernel.. + * + * @param kern the kernel to stop the agent inside. + * @return the number of killed agents. + * @since 0.10 + */ + @SuppressWarnings("use_reserved_sarl_annotation") + @PrivateAPI + static def executeForcedAgentStop(kern : Kernel) : int { + val contextService = kern.getService(typeof(ContextService)) + val lifeCycleService = kern.getService(typeof(LifecycleService)) + assert contextService !== null && lifeCycleService !== null + val contexts = contextService.allContexts + val encounteredAgents = newTreeSet(null) + val agents = newArrayList + val clock = contexts.lock + clock.readLock.lock + try { + val iter = contexts.iterator + while (iter.hasNext) { + val context = iter.next + val ^space = context.defaultSpace as AbstractEventSpace + val slock = ^space.lock + slock.readLock.lock + try { + for (participant : ^space.internalParticipantStructure.values) { + val listener = participant.participant + if (listener instanceof InformedEventListener) { + val ag = listener.ownerInstance + assert ag !== null + if (encounteredAgents += ag.ID) { + agents += ag + } + } + } + } finally { + slock.readLock.unlock + } + } + } finally { + clock.readLock.unlock + } + var nb = 0 + for (ag : agents) { + if (lifeCycleService.killAgent(ag)) { + nb++ + } + } + return nb + } + + /** + * Replies a behavior that shutdowns the kernel. + * + *

The returned trigger invokes the functions {@link #executeForcedAgentStop(Kernel)} + * and {@link #executeKernelStopWhenNoAgentAlive(Kernel)} depending on the size of the + * agents' collection. + * + *

The provided trigger does not run the stopping behavior into a separate thread. + * + * @return the logger of the kernel. + * @since 0.10 + */ + @SuppressWarnings("use_reserved_sarl_annotation") + @PrivateAPI(isCallerOnly = true) + def getShutdownTrigger : Runnable { + [ + var nbKilled = executeForcedAgentStop + if (nbKilled == 0) { + executeKernelStopWhenNoAgentAlive + } + ] + } + + /** + * Asynchronous listener on platform events. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ + protected static class AsynchronousKernelStoppingListener + implements KernelAgentLifecycleListener, Runnable, UncaughtExceptionHandler { + + val kernel : WeakReference + + val stopProcessRunning = new AtomicBoolean(false) + + new (kernel : Kernel) { + this.kernel = new WeakReference(kernel) + } + + override kernelAgentDestroyed { + if (!this.stopProcessRunning.getAndSet(true)) { + // CAUTION: EXECUTE THE STOP FUNCTION IN A THREAD THAT + // IS INDEPENDENT TO THE ONES FROM THE EXECUTORS + // CREATED BY THE EXECUTORSERVICE. + // THIS AVOID THE STOP FUNCTION TO BE INTERRUPTED + // BECAUSE THE EXECUTORSERVICE WAS SHUTTED DOWN. + startAsync + } + } + + /** Start the stopper asynchronously. + * @since 0.10 + */ + @SuppressWarnings("discouraged_reference") + def startAsync { + val thread = Executors::defaultThreadFactory().newThread(this) + thread => [ + name = "SRE kernel shutdown" // $NON-NLS-1$ + daemon = false + uncaughtExceptionHandler = this + start + ] + } + + @SuppressWarnings("use_reserved_sarl_annotation") + @PrivateAPI(isCallerOnly = true) + override run { + this.kernel.get.executeKernelStopWhenNoAgentAlive + } + + override uncaughtException(thread : Thread, exception : Throwable) { + assert thread !== null; + assert exception !== null; + val record = new LogRecord(Level::SEVERE, exception.localizedMessage) + record.thrown = exception + val elt = exception.stackTrace.get(0) + assert elt !== null + record.sourceClassName = elt.className + record.sourceMethodName = elt.methodName + val logger = this.kernel.get.logger + logger.log(record) + } + + } + +} + +/** + * Annotation for injection of the kernel. + * + * @author $Author: srodriguez$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +@BindingAnnotation +@Target(#[ FIELD, PARAMETER, METHOD ]) +@Retention(RUNTIME) +annotation KernelScope { + // +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/Messages.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/Messages.sarl new file mode 100644 index 0000000000..d0fde2734e --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/Messages.sarl @@ -0,0 +1,49 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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 + +import org.eclipse.osgi.util.NLS + +/** Messages. + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +final class Messages extends NLS { + + static val BUNDLE_NAME = typeof(Messages).getPackage.name + ".messages" + + static new { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, typeof(Messages)) + } + + public static var Kernel_0 : String + public static var Kernel_1 : String + public static var Kernel_2 : String + public static var Kernel_3 : String + + private new { + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/SreVersion.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/SreVersion.sarl new file mode 100644 index 0000000000..5b4bfd2ec2 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/SreVersion.sarl @@ -0,0 +1,47 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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 + +/** + * Describes the version of the SRE platform. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.5.0 + */ +final class SreVersion { + + /** The version number of the current release of the SRE platform. + */ + public static val RELEASE_VERSION = "0.8.0" + + /** Flag that indicates if the current SRE platform is a stable release. + * + *

A stable release is a platform that will be not more compiled and generated. + */ + public static val IS_STABLE = false + + private new { + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/Boot.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/Boot.sarl new file mode 100644 index 0000000000..1ed2ff1d9d --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/Boot.sarl @@ -0,0 +1,71 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot + +import io.bootique.help.HelpOption +import java.util.List + +/** + * This is the class that permits to boot programmatically the SRE platform with the Bootique API. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.7.0 + */ +class Boot { + + /** Replies the default name of the program. + * + * @return the default name of the program. + */ + static def getDefaultSreProgramName : String { + return "sarl" + } + + /** Create the instance of the bootique main launcher. + * + * @return the main launcher. + */ + protected static def createMainObject : SreMain { + new SreMain + } + + /** Replies the options of the program. + * + * @return the options of the program. + */ + static def getOptions : List { + createMainObject.options + } + + /** Main program. + * + * @param args the command line arguments. + */ + @SuppressWarnings("discouraged_reference") + static def main(args : String*) : void { + val retCode = createMainObject.runSRE(args) + System::exit(retCode) + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/SreMain.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/SreMain.sarl new file mode 100644 index 0000000000..0872254a6a --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/SreMain.sarl @@ -0,0 +1,373 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot + +import com.google.common.base.Throwables +import com.google.common.util.concurrent.Service +import com.google.inject.Injector +import com.google.inject.Module +import com.google.inject.ProvisionException +import io.bootique.BQRuntime +import io.bootique.Bootique +import io.bootique.help.HelpOption +import io.bootique.help.HelpOptions +import io.bootique.meta.application.ApplicationMetadata +import io.sarl.bootstrap.SRE +import io.sarl.bootstrap.SREBootstrap +import io.sarl.lang.core.Agent +import io.sarl.lang.core.AgentContext +import io.sarl.sre.Kernel +import io.sarl.sre.boot.configs.SreConfig +import io.sarl.sre.boot.configs.subconfigs.BootConfig +import io.sarl.sre.boot.configs.subconfigs.RootContextType +import io.sarl.sre.services.lifecycle.KernelAgentLifecycleListener +import io.sarl.sre.services.lifecycle.LifecycleService +import io.sarl.sre.services.logging.LoggingService +import java.util.List +import java.util.UUID +import org.apache.log4j.Logger +import org.arakhne.afc.bootique.log4j.configs.Level +import org.arakhne.afc.bootique.log4j.configs.Log4jIntegrationConfig +import org.arakhne.afc.bootique.variables.VariableNames +import org.arakhne.afc.util.OutputParameter +import org.eclipse.xtend.lib.annotations.Accessors + +import static io.sarl.bootstrap.SRE.* + +/** Class that implements the standard main function for running a SARL run-time environment + * with bootique modules. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class SreMain implements SREBootstrap { + + @Accessors(PUBLIC_GETTER) + var kernel : Kernel + + @Accessors(PUBLIC_GETTER) + var runtime : BQRuntime + + var isResetableBootAgent = true + + var lastBootAgent : UUID + + var bootType : RootContextType + + var userDefinedContextId : UUID + + var userDefinedSpaceId : UUID + + /** Set the Bootique runtime instance. + * + * @param runtime the new Bootique runtime. + * @return {@code true} if the runtime has been changed. + */ + def setRuntime(runtime : BQRuntime) : boolean { + if (this.runtime !== runtime) { + this.runtime = runtime + return true + } + return false + } + + override isRunning : boolean { + return this.kernel !== null && this.kernel.isRunning + } + + override setRandomContextUUID { + this.bootType = RootContextType::RANDOM + this.userDefinedContextId = null + } + + override setBootAgentTypeContextUUID { + this.bootType = RootContextType::BOOT_AGENT_NAME + this.userDefinedContextId = null + } + + override setSpecificContextUUID { + this.bootType = RootContextType::DEFAULT + this.userDefinedContextId = null + } + + override setUniverseContextUUID(id : UUID) { + this.userDefinedContextId = id + } + + override getUniverseContextUUID : UUID { + this.userDefinedContextId + } + + override setUniverseSpaceUUID(id : UUID) { + this.userDefinedSpaceId = id + } + + override getUniverseSpaceUUID : UUID { + this.userDefinedSpaceId + } + + override setVerboseLevel(level : int) { + var config = ensureBootiqueRuntimeInstance(null, null).getInstance(typeof(Log4jIntegrationConfig)) + var levels = Level::values + var lvl = if (level < 0) 0 else (if (level >= levels.length) levels.length - 1 else level) + var levelObject = levels.get(lvl) + config.level = levelObject + var logger = getKernelLogger + if (logger !== null) { + logger.level = levelObject.toJul + } + } + + override getKernelLogger : java.util.logging.Logger { + if (isActive) { + var service = this.runtime.getInstance(typeof(LoggingService)) + return service.platformLogger + } + return null + } + + /** Create the SRE runtime. + * + * @param bootique the instance of the bootique accessor, or {@code null} if none is provided. + * @param module specify the type of the injection module to be used by the bootique accessor. + * @param args the command line arguments to put into the bootique accessor. This argument is used only if {@code bootique} + * is evaluated to {@code null}. + * @return the runtime. + */ + protected def createRuntime(bootique : Bootique, module : Class, args : String*) : BQRuntime { + var bt = bootique ?: Bootique::app(args) + if (module !== null) { + bt = bt.module(module) + } + return bt.autoLoadModules.createRuntime + } + + private def ensureBootiqueRuntimeInstance(bootique : Bootique, module : Class, args : String*) : BQRuntime { + var r = this.runtime + if (r === null) { + this.runtime = createRuntime(bootique, module, args) + r = this.runtime + } + return this.runtime + } + + /** Run the SRE. + * + *

This function runs the SRE and exits with the return code. + * + * @param args the command line arguments. + * @return the exit code. + */ + def runSRE(args : String*) : int { + var runtime = ensureBootiqueRuntimeInstance(null, null, args) + try { + // Force the bootstrap to be this object + SRE::bootstrap = this + val outcome = runtime.run + if (outcome !== null) { + if (!outcome.success && outcome.exception !== null) { + Logger::rootLogger.error(outcome.message, outcome.exception) + } + return outcome.exitCode + } + } catch (exception : ProvisionException) { + val ex = Throwables::getRootCause(exception) + if (ex !== null) { + Logger::rootLogger.error(ex.localizedMessage) + } else { + Logger.rootLogger.error(exception.localizedMessage) + } + } catch (exception : Throwable) { + Logger::rootLogger.error(exception.localizedMessage) + } + return 255 + } + + /** Replies the options of the program. + * + * @return the options of the program. + */ + def getOptions : List { + val runtime = ensureBootiqueRuntimeInstance(null, null) + val application = runtime.getInstance(typeof(ApplicationMetadata)) + val helpOptions = new HelpOptions + + application.commands.forEach [ c | + helpOptions.add(c.asOption) + c.options.forEach [o | helpOptions.add(o)] + ] + + application.options.forEach [o | helpOptions.add(o)] + + return helpOptions.options + } + + override getBootAgentIdentifier : UUID { + this.lastBootAgent + } + + private def ensureKernelInstance(bootique : Bootique, module : Class, args : String*) : Kernel { + var k = this.kernel + if (k === null) { + var runtime = ensureBootiqueRuntimeInstance(bootique, module, args) + val injector = runtime.getInstance(typeof(Injector)) + // Update the method for selecting the root context identifier + if (this.bootType !== null + || this.userDefinedContextId !== null + || this.userDefinedSpaceId !== null) { + val sreConfig = injector.getInstance(typeof(SreConfig)) + if (this.bootType !== null) { + sreConfig.boot.rootContextBootType = this.bootType + } + if (this.userDefinedContextId !== null) { + sreConfig.boot.rootContextID = this.userDefinedContextId + } + if (this.userDefinedSpaceId !== null) { + sreConfig.boot.rootSpaceID = this.userDefinedSpaceId + } + } + k = injector.getInstance(typeof(Kernel)) + val listener : KernelAgentLifecycleListener = [ + this.kernel = null + this.runtime = null + this.isResetableBootAgent = true + ] + k.getService(typeof(LifecycleService)).addKernelAgentLifecycleListener(listener) + this.kernel = k + } + // Force the bootstrap to be this object + SRE::bootstrap = this + return this.kernel + } + + /** Start the agent framework without agent. + * + * @param bootique the Bootique instance to set up the framework. If it is {@code null}, + * the default Bootique instance is created. + * @param module the injection module to use. If it is {@code null}, the default module is used. + */ + def startWithoutAgent(bootique : Bootique = null, module : Class = null) : AgentContext { + ensureKernelInstance(bootique, module).rootContext + } + + def startAgent(nbAgents : int, agentCls : Class, + params : Object*) : Iterable throws Exception { + val propName = VariableNames::toPropertyName(BootConfig::BOOT_AGENT_NAME) + val old = System::setProperty(propName, agentCls.name) + var kernel : Kernel + try { + kernel = ensureKernelInstance(null, null) + } finally { + if (old === null) { + System::clearProperty(propName) + } else { + System::setProperty(propName, old) + } + } + var ids = new OutputParameter>(emptyList) + kernel.spawn(nbAgents, agentCls, [ + ids.set(it) + if (this.isResetableBootAgent) { + this.lastBootAgent = it.head + + } + ], params) + + return ids.get + } + + /** Start the boot agent if it is specified in the boot configuration. + */ + def startBootAgent : UUID { + var config = this.runtime.getInstance(typeof(SreConfig)).boot + var bootAgent = config.getBootAgent(this.class.classLoader) + assert bootAgent !== null + var args = config.agentStartArgs + startAgent(bootAgent, args) + } + + def startAgent(agentCls : Class, params : Object*) : UUID throws Exception { + val propName = VariableNames::toPropertyName(BootConfig::BOOT_AGENT_NAME) + assert propName !== null + var alreadyLaunched = System::getProperty(propName) + if (alreadyLaunched.nullOrEmpty) { + // Force the property that indicates the booted agent + System::setProperty(propName, agentCls.name) + } + val kernel = ensureKernelInstance(null, null) + val id = new OutputParameter + kernel.spawn(agentCls, [ + id.set(it) + if (this.isResetableBootAgent) { + this.lastBootAgent = it + } + ], params) + return id.get + } + + def startAgentWithID(agentCls : Class, agentID : UUID, params : Object*) throws Exception { + val propName = VariableNames::toPropertyName(BootConfig::BOOT_AGENT_NAME) + assert propName !== null + var alreadyLaunched = System::getProperty(propName) + if (alreadyLaunched.nullOrEmpty) { + // Force the property that indicates the booted agent + System::setProperty(propName, agentCls.name) + } + val kernel = ensureKernelInstance(null, null) + kernel.spawn(agentID, agentCls, [ + if (this.isResetableBootAgent) { + this.lastBootAgent = it + } + ], params) + } + + @SuppressWarnings("discouraged_reference") + def shutdown(blocking : boolean) throws InterruptedException { + var kern = this.kernel + this.kernel = null + if (kern !== null) { + var stopBehavior = kern.shutdownTrigger + stopBehavior.run + if (blocking) { + while (kern.running) { + Thread::yield + } + } + } + } + + override getService(serviceType : Class) : T with T { + if (typeof(Service).isAssignableFrom(serviceType)) { + var kern = this.kernel + if (kern !== null) { + var castedServiceType = serviceType.asSubclass(typeof(Service)) + var serv = kern.getService(castedServiceType) + return serviceType.cast(serv) + } + } + return null + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/commands/AbstractRunCommand.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/commands/AbstractRunCommand.sarl new file mode 100644 index 0000000000..498ac1771f --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/commands/AbstractRunCommand.sarl @@ -0,0 +1,195 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.commands + +import io.bootique.cli.Cli +import io.bootique.command.CommandWithMetadata +import io.bootique.meta.application.CommandMetadata +import io.sarl.bootstrap.SRE +import io.sarl.bootstrap.SREBootstrap +import io.sarl.lang.core.Agent +import io.sarl.sre.boot.configs.SreConfig +import io.sarl.sre.boot.configs.subconfigs.BootConfig +import java.util.List +import java.util.concurrent.locks.ReadWriteLock + +import static java.lang.Thread.* +import javax.inject.Provider + +/** + * Abstract command for running the SRE without agent at boot time. + * + *

This abstract command provides helping tools for implementing a launching command. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +abstract class AbstractRunCommand extends CommandWithMetadata { + + /** Launching configuration. + */ + protected val configuration : Provider + + var sre : SREBootstrap + + val lock : ReadWriteLock + + /** Constructor. + * + * @param configuration the configuration of the tool. + * @param commandBuilder the builder of the command. + * @param lock the lock to use for synchronizing. + */ + new (configuration : Provider, commandBuilder : CommandMetadata.Builder, lock : ReadWriteLock) { + super(commandBuilder) + this.configuration = configuration + this.lock = lock + } + + /** Replies if the given value is the fully qualified name of an agent's type. + * An agent's type is a {@code Class} that extends the {@code Agent} type. + * + * @param value the value to test. + * @return {@code true} if the given value is the fully qualified name of an agent's type. + */ + protected def isAgentType(value : String) : boolean { + try { + var t = Class::forName(value) + if (t !== null && typeof(Agent).isAssignableFrom(t)) { + return true + } + } catch (ex : Exception) { + // + } + return false + } + + /** Synchronizes the command-line arguments and the SRE configuration. + * This function gets the arguments from the given {@code Cli} and + * updates the {@code SreConfig}. + * + * @param args the definition of the command line arguments + * @param maxNbAgents is the maximum number of agents to be launched. + * @return the boot configuration. + */ + protected def synchronizeCliWithBootConfig(args : Cli, maxNbAgents : int) : BootConfig { + var config = this.configuration.get + var boot = config.boot + // Set the command line arguments + var a = args.standaloneArguments + boot.commandLineArgs = a + // Extract specific information from the command line + var bootIds : List + var agentArgs : List + if (maxNbAgents > 0) { + bootIds = newArrayList + agentArgs = newArrayList + for (arg : args.standaloneArguments) { + if (arg.isAgentType) { + bootIds += arg + } else { + agentArgs += arg + } + } + } else { + bootIds = emptyList + agentArgs = args.standaloneArguments + } + if (!bootIds.empty) { + boot.bootAgent = bootIds.get(0) + } + if (!agentArgs.empty) { + boot.agentStartArgs = agentArgs + } + return boot + } + + /** Replies the SRE bootstrap. + * + * @return the SRE bootstrap. + */ + protected def getSREBootstrap : SREBootstrap { + var s : SREBootstrap + this.lock.readLock.lock + try { + s = this.sre + } finally { + this.lock.readLock.unlock + } + if (s === null) { + this.lock.writeLock.lock + try { + s = this.sre + if (s === null) { + this.sre = SRE::bootstrap + s = this.sre + val config = this.configuration.get + switch (config.boot.rootContextBootType) { + case BOOT_AGENT_NAME: { + this.sre.setBootAgentTypeContextUUID + } + case RANDOM: { + this.sre.setRandomContextUUID + } + case DEFAULT: { + this.sre.setSpecificContextUUID + } + default: { + this.sre.setSpecificContextUUID + } + } + this.sre.universeContextUUID = config.boot.rootContextID + this.sre.universeSpaceUUID = config.boot.rootSpaceID + } + } finally { + this.lock.writeLock.unlock + } + } + return s + } + + /** Start all the boot agents that are defined into the configuration. + * + * @param bootstrap the SRE bootstrap. + * @param config the configuration to use. + */ + protected def startAgentsFromConfig(bootstrap : SREBootstrap, config : BootConfig) : void { + var bootAgent = config.getBootAgent(class.classLoader) + bootstrap.startAgent(bootAgent, config.agentStartArgs) + } + + /** Wait for the termination of the SRE execution. + * This function returns when the SRE is stopped. + * + * @param bootstrap the SRE bootstrap. + */ + @SuppressWarnings("discouraged_reference") + protected def waitForTermination(bootstrap : SREBootstrap) : void { + while (bootstrap.active) { + sleep(100) + } + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/commands/Messages.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/commands/Messages.sarl new file mode 100644 index 0000000000..54c12a5278 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/commands/Messages.sarl @@ -0,0 +1,60 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.commands + +import org.eclipse.osgi.util.NLS + +/** Messages for the SARL batch compiler. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @ExcludeFromApidoc + */ +final class Messages extends NLS { + + static val BUNDLE_NAME = typeof(Messages).getPackage.name + ".messages" + + static new { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, typeof(Messages)) + } + + public static var RunSingleAgentCommand_0 : String + public static var RunSingleAgentCommand_1 : String + public static var RunSingleAgentCommand_2 : String + public static var RunSingleAgentCommand_3 : String + public static var RunSingleAgentCommandModuleProvider_0 : String + public static var RunWithoutAgentCommand_0 : String + public static var RunWithoutAgentCommand_1 : String + public static var RunWithoutAgentCommand_2 : String + public static var RunWithoutAgentCommand_3 : String + public static var RunWithoutAgentCommandModuleProvider_0 : String + public static var VersionCommand_0 : String + public static var VersionCommand_1 : String + public static var VersionCommandModuleProvider_0 : String + + private new { + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/commands/RunSingleAgentCommand.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/commands/RunSingleAgentCommand.sarl new file mode 100644 index 0000000000..60303c6c35 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/commands/RunSingleAgentCommand.sarl @@ -0,0 +1,140 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.commands + +import io.bootique.BQModule +import io.bootique.BQModuleProvider +import io.bootique.cli.Cli +import io.bootique.command.CommandOutcome +import io.bootique.meta.application.CommandMetadata +import io.sarl.sre.boot.configs.SreConfig + +import static extension io.bootique.BQCoreModule.extend +import java.text.MessageFormat +import java.util.concurrent.locks.ReadWriteLock +import javax.inject.Provider +import com.google.inject.AbstractModule +import javax.inject.Singleton +import com.google.inject.Provides +import com.google.inject.Module + +/** + * Command for running the SRE with a single agent at boot time. + * The first command-line argument that is a agent's class is considered + * as the agent to be launched. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.8 + */ +class RunSingleAgentCommand extends AbstractRunCommand { + + /** Constructor. + * + * @param configuration the configuration of the tool. + * @param lock the lock to use for synchronizing. + */ + new (configuration : Provider, lock : ReadWriteLock) { + super(configuration, + CommandMetadata + .builder(typeof(RunSingleAgentCommand)) + .description(Messages::RunSingleAgentCommand_0), + lock) + } + + override run(cli : Cli) : CommandOutcome { + var args = cli.standaloneArguments() + if (args.empty) { + return CommandOutcome::failed(255, Messages::RunSingleAgentCommand_1) + } + + try { + val bootstrap = getSREBootstrap + if (bootstrap === null) { + return CommandOutcome::failed(255, Messages::RunSingleAgentCommand_3) + } + var config = cli.synchronizeCliWithBootConfig(1) + bootstrap.startAgentsFromConfig(config) + bootstrap.waitForTermination + } catch (exception : Throwable) { + return CommandOutcome.failed(255, + MessageFormat::format(Messages::RunSingleAgentCommand_2, exception.localizedMessage), exception) + } + + return CommandOutcome::succeeded + } + +} + +/** Module for the command for running the SRE with a single agent. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class RunSingleAgentCommandModule extends AbstractModule { + + override configure { + binder.extend => [ + addCommand(typeof(RunSingleAgentCommand)) + defaultCommand = typeof(RunSingleAgentCommand) + ] + } + + /** Provide the command for running the SRE. + * + * @param configuration the configuration of the tool. + * @param lock the provider of lock. + * @return the command. + */ + @Provides + @Singleton + def provideRunCommand(configuration : Provider, lock : Provider) : RunSingleAgentCommand { + return new RunSingleAgentCommand(configuration, lock.get) + } + +} + +/** Provider of the module for running the SRE with a single agent. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class RunSingleAgentCommandModuleProvider implements BQModuleProvider { + + override module : Module { + return new RunSingleAgentCommandModule + } + + override moduleBuilder : BQModule.Builder { + return BQModule.builder(module).overrides(overrides).providerName(name).configs(configs).description( + Messages::RunSingleAgentCommandModuleProvider_0); + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/commands/RunWithoutAgentCommand.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/commands/RunWithoutAgentCommand.sarl new file mode 100644 index 0000000000..b1d9156830 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/commands/RunWithoutAgentCommand.sarl @@ -0,0 +1,136 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.commands + +import io.bootique.BQModule +import io.bootique.BQModuleProvider +import io.bootique.cli.Cli +import io.bootique.command.CommandOutcome +import io.bootique.meta.application.CommandMetadata +import io.sarl.sre.boot.configs.SreConfig +import java.text.MessageFormat + +import static extension io.bootique.BQCoreModule.extend +import java.util.concurrent.locks.ReadWriteLock +import javax.inject.Provider +import com.google.inject.AbstractModule +import javax.inject.Singleton +import com.google.inject.Provides +import com.google.inject.Module + +/** + * Command for running the SRE without agent at boot time. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class RunWithoutAgentCommand extends AbstractRunCommand { + + static val CLI_NAME = "noagent" + + /** Constructor. + * + * @param configuration the configuration of the tool. + * @param lock the lock to use for synchronizing. + */ + new (configuration : Provider, lock : ReadWriteLock) { + super(configuration, + CommandMetadata + .builder(typeof(RunWithoutAgentCommand)) + .description(Messages::RunWithoutAgentCommand_0) + .name(CLI_NAME), + lock) + } + + override run(cli : Cli) : CommandOutcome { + if (!cli.standaloneArguments().isEmpty()) { + return CommandOutcome::failed(255, Messages::RunWithoutAgentCommand_1) + } + + try { + cli.synchronizeCliWithBootConfig(0) + val sre = getSREBootstrap + val universeContext = sre.startWithoutAgent + if (universeContext === null) { + return CommandOutcome::failed(255, Messages::RunWithoutAgentCommand_3) + } + sre.waitForTermination + } catch (exception : Throwable) { + return CommandOutcome::failed(255, MessageFormat::format(Messages::RunWithoutAgentCommand_2, exception.localizedMessage), exception) + } + + return CommandOutcome::succeeded + } + +} + +/** Module for the command for running the SRE without an agent. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class RunWithoutAgentCommandModule extends AbstractModule { + + override configure { + binder.extend.addCommand(typeof(RunWithoutAgentCommand)) + } + + /** Provide the command for running the SRE. + * + * @param configuration the configuration of the tool. + * @param lock the provider of lock. + * @return the command. + */ + @Provides + @Singleton + def provideRunCommand(configuration : Provider, lock : Provider) : RunWithoutAgentCommand { + return new RunWithoutAgentCommand(configuration, lock.get) + } + +} + +/** Provider of the module for running the SRE without an agent. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class RunWithoutAgentCommandModuleProvider implements BQModuleProvider { + + override module : Module { + return new RunWithoutAgentCommandModule + } + + override moduleBuilder : BQModule.Builder { + return BQModule.builder(module).overrides(overrides).providerName(name).configs(configs).description( + Messages::RunWithoutAgentCommandModuleProvider_0); + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/commands/VersionCommand.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/commands/VersionCommand.sarl new file mode 100644 index 0000000000..8ef24827c0 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/commands/VersionCommand.sarl @@ -0,0 +1,128 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.commands + +import io.bootique.BQModule +import io.bootique.BQModuleProvider +import io.bootique.cli.Cli +import io.bootique.command.CommandOutcome +import io.bootique.command.CommandWithMetadata +import io.bootique.log.BootLogger +import io.bootique.meta.application.CommandMetadata +import io.sarl.lang.SARLVersion +import io.sarl.sre.SreVersion +import java.text.MessageFormat + +import static extension io.bootique.BQCoreModule.extend +import com.google.inject.AbstractModule +import javax.inject.Singleton +import com.google.inject.Provides +import com.google.inject.Module + +/** + * Command for showing the SRE version. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class VersionCommand extends CommandWithMetadata { + + static val CLI_NAME = "version" + + val bootLogger : BootLogger + + /** Constructor. + * + * @param bootLogger the logger. + */ + new (bootLogger : BootLogger) { + super(CommandMetadata + .builder(typeof(VersionCommand)) + .description(Messages::VersionCommand_0) + .name(CLI_NAME)) + this.bootLogger = bootLogger + } + + override run(cli : Cli) : CommandOutcome { + val text = new StringBuilder + text.append(MessageFormat::format(Messages::VersionCommand_1, + SreVersion::RELEASE_VERSION, + SreVersion::IS_STABLE, + SARLVersion.SARL_RELEASE_VERSION, + SARLVersion.SPECIFICATION_RELEASE_VERSION_STRING, + System.getProperty("java.version"))) //$NON-NLS-1$ + this.bootLogger.stdout(text.toString()) + return CommandOutcome.succeeded() + } + +} + +/** Module for the command for printing out the SRE version. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class VersionCommandModule extends AbstractModule { + + override configure { + binder.extend.addCommand(typeof(VersionCommand)) + } + + /** Provide the command for displaying the SRE version. + * + * @param bootLogger the logger. + * @return the command. + */ + @Provides + @Singleton + def provideVersionCommand(bootLogger : BootLogger) : VersionCommand { + return new VersionCommand(bootLogger) + } + +} + +/** Provider of the module for the version command. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class VersionCommandModuleProvider implements BQModuleProvider { + + override module : Module { + return new VersionCommandModule + } + + override moduleBuilder : BQModule.Builder { + return BQModule.builder(module).overrides(overrides).providerName(name).configs(configs).description( + Messages::VersionCommandModuleProvider_0); + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/commands/messages.properties b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/commands/messages.properties new file mode 100644 index 0000000000..7e996b2868 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/commands/messages.properties @@ -0,0 +1,13 @@ +RunSingleAgentCommand_0 = Run the application within the SRE with a single agent at boot time. +RunSingleAgentCommand_1 = Not enough arguments. You have to provide the qualified name of the agent to be launched. +RunSingleAgentCommand_2 = Cannot create the universe context: {0} +RunSingleAgentCommand_3 = Cannot create the universe context. +RunWithoutAgentCommand_0 = Run the application within the SRE without any agent at boot time.. +RunWithoutAgentCommand_1 = Too many arguments. You must not provide arguments. +RunWithoutAgentCommand_2 = Cannot create the universe context: {0} +RunWithoutAgentCommand_3 = Cannot create the universe context. +RunSingleAgentCommandModuleProvider_0 = The command for running the SRE with a single agent at boot time. +RunWithoutAgentCommandModuleProvider_0 = The command for running the SRE without any single agent at boot time. +VersionCommand_0 = Prints release information. +VersionCommand_1 = SRE Version: {0}\nSARL Version: {2}\nSARL Specification Version: {3}\nJava Version: {4}\n +VersionCommandModuleProvider_0 = The command for printing out the version of this application. diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/Messages.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/Messages.sarl new file mode 100644 index 0000000000..2d0f41a88d --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/Messages.sarl @@ -0,0 +1,50 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.configs + +import org.eclipse.osgi.util.NLS + +/** Messages for the SRE configuration. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @ExcludeFromApidoc + */ +final class Messages extends NLS { + + static val BUNDLE_NAME = typeof(Messages).getPackage.name + ".messages" + + static new { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, typeof(Messages)) + } + + public static var SreConfigModule_0 : String + public static var SreConfigModule_1 : String + public static var SreConfigModuleProvider_0 : String + + private new { + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/SreConfig.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/SreConfig.sarl new file mode 100644 index 0000000000..99fc74cc44 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/SreConfig.sarl @@ -0,0 +1,211 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.configs + +import io.bootique.BQModule +import io.bootique.BQModuleProvider +import io.bootique.annotation.BQConfig +import io.bootique.annotation.BQConfigProperty +import io.bootique.config.ConfigurationFactory +import io.bootique.meta.application.OptionMetadata +import io.sarl.sre.boot.configs.subconfigs.BootConfig +import io.sarl.sre.boot.configs.subconfigs.ServicesConfig +import java.text.MessageFormat +import org.arakhne.afc.bootique.variables.VariableDecls +import org.arakhne.afc.bootique.variables.VariableNames +import org.eclipse.xtend.lib.annotations.Accessors + +import static extension io.bootique.BQCoreModule.extend +import static io.sarl.sre.boot.configs.SreConfig.* +import java.util.Map +import java.lang.reflect.Type +import java.util.Collections +import com.google.inject.AbstractModule +import javax.inject.Singleton +import com.google.inject.Provides +import com.google.inject.Injector +import com.google.inject.Module + +/** + * Configuration for the SARL run-time environment. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +@BQConfig("Configuration for the SARL run-time environment") +class SreConfig { + + /** + * Prefix for the configuration entries of the modules. + */ + public static val PREFIX = "sre" + + /** + * Name of the property that contains the classpath. + */ + public static val CLASSPATH_NAME = PREFIX + ".classpath"; // $NON-NLS-1$ + + + @Accessors(PUBLIC_GETTER) + var classpath : String + + var bootConfig : BootConfig + + var servicesConfig : ServicesConfig + + /** Change the class path. + * + * @param path the class path. + */ + @BQConfigProperty("Class path for the SARL compiler.") + def setClasspath(path : String) { + this.classpath = path + } + + /** Replies the configuration for the SARL run-time environment. + * + * @param configFactory the general configuration factory. + * @return the configuration. + */ + static def getConfiguration(configFactory : ConfigurationFactory) : SreConfig { + assert configFactory !== null + var factory = configFactory.config(typeof(SreConfig), PREFIX) + return factory + } + + /** Replies the boot configuration. + * + * @return the boot configuration. + */ + def getBoot : BootConfig { + if (this.bootConfig === null) { + this.bootConfig = new BootConfig + } + return this.bootConfig + } + + /** Change the validator configuration. + * + * @param config the validator configuration. + */ + @BQConfigProperty("Configuration for booting the SRE.") + def setBoot(config : BootConfig) { + this.bootConfig = config + } + + /** Replies the SRE services' configuration. + * + * @return the SRE services' configuration. + */ + def getServices : ServicesConfig { + if (this.servicesConfig === null) { + this.servicesConfig = new ServicesConfig + } + return this.servicesConfig + } + + /** Change the services' configuration. + * + * @param config the services' configuration. + */ + @BQConfigProperty("Configuration for the SRE services.") + def setServices(config : ServicesConfig) { + this.servicesConfig = config + } + +} + +/** + * Module for creating and configuring the general SRE configuration. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class SreConfigModule extends AbstractModule { + + static val CLASSPATH_LONG_OPTION = "classpath" + + static val CLASSPATH_SHORT_OPTION = "cp" + + override configure : void { + VariableDecls::extend(binder).declareVar(CLASSPATH_NAME) + val cpDescription = MessageFormat::format(Messages::SreConfigModule_0, + VariableNames::toEnvironmentVariableName(CLASSPATH_NAME), + CLASSPATH_SHORT_OPTION, + CLASSPATH_LONG_OPTION) + binder.extend.addOption(OptionMetadata::builder(CLASSPATH_LONG_OPTION, cpDescription) + .valueRequired(Messages::SreConfigModule_1) + .build) + .mapConfigPath(CLASSPATH_LONG_OPTION, CLASSPATH_NAME) + binder.extend.addOption(OptionMetadata.builder(CLASSPATH_SHORT_OPTION, cpDescription) + .valueRequired(Messages::SreConfigModule_1) + .build) + .mapConfigPath(CLASSPATH_SHORT_OPTION, CLASSPATH_NAME) + + } + + /** Replies the instance of the SRE configuration. + * + * @param configFactory accessor to the bootique factory. + * @param injector the current injector. + * @return the path configuration accessor. + */ + @Provides + @Singleton + def provideSreConfig(configFactory : ConfigurationFactory, injector : Injector) : SreConfig { + val config = getConfiguration(configFactory) + injector.injectMembers(config) + return config + } + +} + +/** Provider of the module for creating and configuring the general SRE configuration. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class SreConfigModuleProvider implements BQModuleProvider { + + override module : Module { + return new SreConfigModule + } + + override configs : Map { + return Collections::singletonMap(PREFIX, typeof(SreConfig)) + } + + override moduleBuilder : BQModule.Builder { + return BQModule::builder(module).overrides(overrides).providerName(name).configs(configs).description( + Messages::SreConfigModuleProvider_0); + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/messages.properties b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/messages.properties new file mode 100644 index 0000000000..101ac7d9e9 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/messages.properties @@ -0,0 +1,6 @@ +SreConfigModule_0 = Specifies where to find user class files, and source files. This class path \ + overrides the user class path in the {0} environment variable. If neither {0}, --{1} nor --{2} \ + is specified, then the user class path is built upon the current folder. If a user class path \ + is specified, it must contains the the user libraries and the standard SARL libraries. +SreConfigModule_1 = path +SreConfigModuleProvider_0 = The configuration for the SRE. \ No newline at end of file diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/subconfigs/BootConfig.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/subconfigs/BootConfig.sarl new file mode 100644 index 0000000000..e39b291329 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/subconfigs/BootConfig.sarl @@ -0,0 +1,525 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.configs.subconfigs + +import com.google.inject.AbstractModule +import com.google.inject.Module +import com.google.inject.Provides +import io.bootique.BQModule +import io.bootique.BQModuleProvider +import io.bootique.annotation.BQConfig +import io.bootique.annotation.BQConfigProperty +import io.bootique.meta.application.OptionMetadata +import io.sarl.bootstrap.SRE +import io.sarl.bootstrap.SREBootstrap +import io.sarl.lang.core.Agent +import io.sarl.sre.boot.configs.SreConfig +import java.lang.reflect.Type +import java.text.MessageFormat +import java.util.Collections +import java.util.Map +import java.util.UUID +import javax.inject.Named +import javax.inject.Singleton +import org.arakhne.afc.bootique.variables.VariableDecls + +import static io.sarl.sre.boot.configs.subconfigs.BootConfig.* + +import static extension io.bootique.BQCoreModule.extend + +/** + * Configuration for specifying the SRE booting parameters. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.8.0 + */ +@BQConfig("Booting configuration for the SARL Run-time Environment") +class BootConfig { + + /** + * Prefix for the configuration entries of the boot modules. + */ + public static val PREFIX : String = SreConfig::PREFIX + ".boot" + + /** + * Name of the property that contains the agent to boot. + */ + public static val BOOT_AGENT_NAME = PREFIX + ".bootAgent" + + /** + * Name of the property that contains the identifier of the root context. + * + * @see #ROOT_CONTEXT_ID_VALUE + */ + public static val ROOT_CONTEXT_ID_NAME = PREFIX + ".rootContextId" + + /** + * The default value for the root context identifier. + * + * @see #ROOT_CONTEXT_ID_NAME + */ + public static val ROOT_CONTEXT_ID_VALUE = "2c38fb7f-f363-4f6e-877b-110b1f07cc77" + + /** + * Name of the property that contains the identifier for the default space of the root context. + * + * @see #ROOT_DEFAULT_SPACE_ID_VALUE + */ + public static val ROOT_DEFAULT_SPACE_ID_NAME = PREFIX + ".rootSpaceId" + + /** + * The default value for the root default space identifier. + * + * @see #ROOT_DEFAULT_SPACE_ID_NAME + */ + public static val ROOT_DEFAULT_SPACE_ID_VALUE = "7ba8885d-545b-445a-a0e9-b655bc15ebe0" + + /** + * Name of the property that contains the name of the SRE main program from the external point of view. + * + * @see #PROGRAM_NAME_VALUE + */ + public static val PROGRAM_NAME_NAME = PREFIX + ".programName" + + /** Default value of the property that contains the name of the SRE + * main program from the external point of view. + * + * @see #PROGRAM_NAME + */ + public static val PROGRAM_NAME_VALUE = "SARL Run-time Environment" + + /** + * Name of the property that contains the type of root context at boot. + */ + public static val ROOT_CONTEXT_BOOT_TYPE_NAME = PREFIX + ".rootContextBootType" + + var commandLineArguments : String[] + + var agentStartArguments : String[] + + var bootAgent : String + + transient var bootAgentType : Class + + var rootContextBootType : RootContextType + + var rootContextID : UUID + + var rootSpaceID : UUID + + var programName : String + + /** Replies the name of the agent to launch at boot time. + * + * @return the qualified name of the name. + */ + def getBootAgent : String { + return this.bootAgent.extractBootAgent + } + + /** Set the name of the agent to launch at boot time. + * + * @param name the qualified name of the name. + */ + @BQConfigProperty("fully-qualified name of the agent's type to launch") + def setBootAgent(name : String) { + this.bootAgent = name.extractBootAgent + } + + private def extractBootAgent(name : String) : String { + if (!name.isNullOrEmpty) { + return name + } + if (this.commandLineArguments === null || this.commandLineArguments.length == 0) { + throw new NoBootAgentNameException + } + var nm = this.commandLineArguments.get(0) + if (nm.isNullOrEmpty) { + throw new NoBootAgentNameException + } + return nm + } + + /** Replies the type of the agent to launch at boot time. + * + * @param classLoader the class loader to use for retrieving the agent type. + * @return the agent type. + */ + def getBootAgent(classLoader : ClassLoader) : Class { + if (this.bootAgentType === null) { + var type = classLoader.loadClass(getBootAgent) + if (type !== null && typeof(Agent).isAssignableFrom(type)) { + this.bootAgentType = type.asSubclass(typeof(Agent)) + } else { + throw new InvalidAgentNameException + } + } + return this.bootAgentType + } + + /** Replies the command line arguments that are passed to the program. + * + *

This function replies the arguments passed to the program. The arguments + * that are passed to an agent when it is started are replied by + * {@link #getAgentStartArgs()}. + * + * @return the command line arguments. + * @see #getAgentStartArgs() + */ + def getCommandLineArgs : String[] { + if (this.commandLineArguments === null || this.commandLineArguments.length <= 1) { + return #[] + } + return this.commandLineArguments + } + + /** Change the command line arguments that are passed to the program. + * + *

The arguments that are passed to an agent when it is started are replied by + * {@link #getAgentStartArgs()} and changed by {@link #setAgentStartArgs(String[])}. + * + * @param args the command line arguments. + * @see #setAgentStartArgs(String[]) + */ + def setCommandLineArgs(args : String[]) { + this.commandLineArguments = args + } + + /** Replies the arguments to pass to an agent when it is started. + * + *

This function is a subset of the command line arguments that are + * replied by {@link #getCommandLineArgs()}. + * + * @return the arguments. + * @see #getCommandLineArgs() + */ + def getAgentStartArgs : String[] { + if (this.agentStartArguments === null) { + this.agentStartArguments = #[] + } + return this.agentStartArguments + } + + /** Change the arguments to pass to an agent when it is started. + * + *

The agent start arguments constitute a subset of the command line arguments that are + * replied by {@link #getCommandLineArgs()}. + * The arguments that are passed to an agent when it is started are replied by + * {@link #getAgentStartArgs()} and changed by {@link #setAgentStartArgs(String[])}. + * + * @param args the command line arguments. + */ + def setAgentStartArgs(args : String[]) { + this.agentStartArguments = args + } + + /** + * Replies the identifier of the root context. + * + * @return the context identifier + */ + def getRootContextID() : UUID { + if (this.rootContextID === null) { + this.rootContextID = computeRootContextID + } + return this.rootContextID + } + + /** + * Set the identifier of the root context. + * + * @param id the context identifier + */ + @BQConfigProperty("Identifier of the root context if applicable according to the context identifier computing method") + def setRootContextID(id : UUID) : void { + if (id === null) { + this.rootContextID = computeRootContextID + } else { + this.rootContextID = id + } + } + + private def computeRootContextID : UUID { + switch (getRootContextBootType) { + case BOOT_AGENT_NAME: { + var name = getBootAgent + if (name.isNullOrEmpty) { + return UUID::randomUUID + } + return UUID::nameUUIDFromBytes(name.bytes) + } + case RANDOM: { + return UUID::randomUUID + } + case DEFAULT: { + return ROOT_CONTEXT_ID_VALUE.parseUUID + } + } + throw new IllegalStateException + } + + /** + * Construct an identifier for the root default space. + * + * @return the space ID + */ + def getRootSpaceID() : UUID { + ensureRootSpaceIDValue + return this.rootSpaceID + } + + /** + * Set the identifier for the root default space. + * + * @param id the space ID + */ + @BQConfigProperty("identifier of the default space within the root context") + def setRootSpaceID(id : UUID) { + this.rootSpaceID = id + ensureRootSpaceIDValue + } + + private def ensureRootSpaceIDValue { + if (this.rootSpaceID === null) { + this.rootSpaceID = ROOT_DEFAULT_SPACE_ID_VALUE.parseUUID + } + } + + private static def parseUUID(value : String) : UUID { + assert !value.nullOrEmpty + try { + return UUID::fromString(value) + } catch (exception : Throwable) { + return UUID::nameUUIDFromBytes(value.bytes) + } + } + + /** Replies the name of the SRE main program from the external point of view. + * + * @return the name of the program. + */ + def getProgramName : String { + ensureProgramName + return this.programName + } + + /** Set the name of the SRE main program from the external point of view. + * + * @param name the name of the program. + */ + @BQConfigProperty("name of the program") + def setProgramName(name : String) { + this.programName = name + ensureProgramName + } + + private def ensureProgramName : void { + if (this.programName === null) { + this.programName = PROGRAM_NAME_VALUE + } + } + + /** + * Replies the type of id computation for the root context. + * + * @return the type of id computation. + */ + def getRootContextBootType() : RootContextType { + ensureRootContextBootType + return this.rootContextBootType + } + + /** + * Set the type of id computation for the root context. + * + * @param type the type of id computation. + */ + @BQConfigProperty("method for computing the identifier of the root context") + def setRootContextBootType(type : RootContextType) { + this.rootContextBootType = type + ensureRootContextBootType + } + + private def ensureRootContextBootType : void { + if (this.rootContextBootType === null) { + this.rootContextBootType = RootContextType::DEFAULT + } + } + +} + +/** + * The name of the boot agent is not provided, e.g. on the command line. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.8.0 + */ +class NoBootAgentNameException extends RuntimeException { +} + +/** + * The name of the boot agent is not a sub-type of {@code Agent}. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.8.0 + */ +class InvalidAgentNameException extends RuntimeException { +} + +/** + * Module for the SRE booting parameters. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class BootConfigModule extends AbstractModule { + + static val ROOT_CONTEXT_ID_OPTION = "rootContextId" + + static val DEFAULT_SPACE_ID_OPTION = "defaultSpaceId" + + static val NAME_OPTION = "name" + + static val BOOT_TYPE_OPTION = "bootType" + + override configure : void { + VariableDecls::extend(binder).declareVar(BOOT_AGENT_NAME) + + VariableDecls::extend(binder).declareVar(ROOT_CONTEXT_ID_NAME); + binder.extend.addOption( + OptionMetadata::builder( + ROOT_CONTEXT_ID_OPTION, + MessageFormat::format(Messages::BootConfigModule_0, ROOT_CONTEXT_ID_VALUE) + ).build + ).mapConfigPath(ROOT_CONTEXT_ID_OPTION, ROOT_CONTEXT_ID_NAME) + + VariableDecls::extend(binder).declareVar(ROOT_DEFAULT_SPACE_ID_NAME); + binder.extend.addOption( + OptionMetadata::builder( + DEFAULT_SPACE_ID_OPTION, + MessageFormat::format(Messages::BootConfigModule_1, ROOT_DEFAULT_SPACE_ID_VALUE) + ).build + ).mapConfigPath(DEFAULT_SPACE_ID_OPTION, ROOT_DEFAULT_SPACE_ID_VALUE) + + VariableDecls::extend(binder).declareVar(PROGRAM_NAME_NAME); + binder.extend.addOption( + OptionMetadata::builder( + NAME_OPTION, + MessageFormat::format(Messages::BootConfigModule_2, PROGRAM_NAME_VALUE) + ).build + ).mapConfigPath(NAME_OPTION, PROGRAM_NAME_NAME) + + VariableDecls::extend(binder).declareVar(ROOT_CONTEXT_BOOT_TYPE_NAME); + binder.extend.addOption( + OptionMetadata::builder( + BOOT_TYPE_OPTION, + MessageFormat::format(Messages::BootConfigModule_3, RootContextType::^default, + RootContextType::jsonLabels)) + .build + ).mapConfigPath(BOOT_TYPE_OPTION, ROOT_CONTEXT_BOOT_TYPE_NAME) + } + + /** Replies the type's name of the agent to boot. + * + * @param configFactory accessor to the bootique factory. + * @return the name of the agent to boot. + */ + @Provides + @Named(BOOT_AGENT_NAME) + def getBootAgentType(config : SreConfig) : Class { + config.boot.getBootAgent(getClass.getClassLoader) + } + + /** Replies the programmatic bootstrap for the SRE. + * + * @return the provider of the bootstrap. + */ + @Provides + @Singleton + static def getProgrammaticBootstrap : SREBootstrap { + SRE::bootstrap + } + + /** + * Replies the identifier of the root context. + * + * @param config accessor to the bootique factory. + * @return the context identifier + */ + @Provides + @Named(ROOT_CONTEXT_ID_NAME) + @Singleton + def getRootContextID(config : SreConfig) : UUID { + config.boot.rootContextID + } + + /** + * Construct an identifier for the root default space. + * + * @param configFactory accessor to the bootique factory. + * @return the space ID + */ + @Provides + @Named(ROOT_DEFAULT_SPACE_ID_NAME) + @Singleton + def getRootSpaceID(config : SreConfig) : UUID { + config.boot.rootSpaceID + } + +} + +/** Provider of the module for the SRE booting parameters. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class BootConfigModuleProvider implements BQModuleProvider { + + override module : Module { + return new BootConfigModule + } + + override configs : Map { + return Collections::singletonMap(PREFIX, typeof(BootConfig)) + } + + override moduleBuilder : BQModule.Builder { + return BQModule::builder(module).overrides(overrides).providerName(name).configs(configs).description( + Messages::BootConfigModuleProvider_0); + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/subconfigs/ExecutorsConfig.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/subconfigs/ExecutorsConfig.sarl new file mode 100644 index 0000000000..9b0087a826 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/subconfigs/ExecutorsConfig.sarl @@ -0,0 +1,327 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.configs.subconfigs + +import com.google.inject.AbstractModule +import com.google.inject.Module +import io.bootique.BQModule +import io.bootique.BQModuleProvider +import io.bootique.annotation.BQConfig +import io.bootique.annotation.BQConfigProperty +import io.bootique.meta.application.OptionMetadata +import java.lang.reflect.Type +import java.text.MessageFormat +import java.util.Collections +import java.util.Map +import org.arakhne.afc.bootique.log4j.configs.Level +import org.arakhne.afc.bootique.variables.VariableDecls +import org.eclipse.xtend.lib.annotations.Accessors + +import static io.sarl.sre.boot.configs.subconfigs.ExecutorsConfig.* + +import static extension io.bootique.BQCoreModule.extend + +/** + * Configuration for the parallel executors. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.8 + */ +@BQConfig("Configuration of the executor service") +class ExecutorsConfig { + + /** + * Prefix for the configuration entries of the parallel execution configuration. + */ + public static val PREFIX : String = ServicesConfig::PREFIX + ".executors" + + /** + * Name of property for the maximal number of threads to keep in the pool. + * + * @see #MAX_NUMBER_OF_THREADS_IN_EXECUTOR_VALUE + */ + public static val MAX_NUMBER_OF_THREADS_IN_EXECUTOR_NAME = PREFIX + ".mxThreads" + + /** + * Indicates the maximal number of threads to keep in the pool. + * + * @see #MAX_NUMBER_OF_THREADS_IN_EXECUTOR_NAME + */ + public static val MAX_NUMBER_OF_THREADS_IN_EXECUTOR_VALUE = 32 + + /** + * Name of the property for the minimal number of threads to keep in the pool, even if they are idle. + * + * @see #MIN_NUMBER_OF_THREADS_IN_EXECUTOR_VALUE + */ + public static val MIN_NUMBER_OF_THREADS_IN_EXECUTOR_NAME = PREFIX + ".mnThreads" + + /** + * Indicates the minimal number of threads to keep in the pool, even if they are idle. + * + * @see #MIN_NUMBER_OF_THREADS_IN_EXECUTOR_NAME + */ + public static val MIN_NUMBER_OF_THREADS_IN_EXECUTOR_VALUE = 0 + + /** + * Name of the property for the duration for keeping the iddle threads alive (in seconds). + * + * @since 0.5.0 + * @see #THREAD_KEEP_ALIVE_DURATION_VALUE + */ + public static val THREAD_KEEP_ALIVE_DURATION_NAME = PREFIX + ".keepAlive" + + /** + * Indicates the duration for keeping the iddle threads alive (in seconds). + * + * @since 0.5.0 + * @see #THREAD_KEEP_ALIVE_DURATION_NAME + */ + public static val THREAD_KEEP_ALIVE_DURATION_VALUE = 60 + + /** + * Name of the property for the numbers of seconds that the kernel is waiting for thread terminations before timeout. + * + * @see #KERNEL_THREAD_TIMEOUT_VALUE + */ + public static val THREAD_TIMEOUT_NAME = PREFIX + ".threadTimeout" + + /** + * Indicates the numbers of seconds that the kernel is waiting for thread terminations before timeout. + * + * @see #KERNEL_THREAD_TIMEOUT_NAME + */ + public static val THREAD_TIMEOUT_VALUE = 30 + + /** + * Name of the property for the default verbosity level of the executor's internal errors. + * + * @see #INTERNAL_ERROR_VERBOSE_LEVEL_VALUE + */ + public static val INTERNAL_ERROR_VERBOSE_LEVEL_NAME = PREFIX + ".internalErrorLogLevel" + + + /** + * The default verbosity level of the executor's internal errors. + * + * @see #INTERNAL_ERROR_VERBOSE_LEVEL_NAME + */ + public static val INTERNAL_ERROR_VERBOSE_LEVEL_VALUE = Level::DEBUG + + + + + var internalErrorVerboseLevel : Level + + var maxThreads : Integer + + @Accessors(PUBLIC_GETTER) + var minThreads : int = MIN_NUMBER_OF_THREADS_IN_EXECUTOR_VALUE + + @Accessors(PUBLIC_GETTER) + var keepAliveDuration : int = THREAD_KEEP_ALIVE_DURATION_VALUE + + @Accessors(PUBLIC_GETTER) + var timeout : int = THREAD_TIMEOUT_VALUE + + /** Change the maximum number of threads that could be used by the executor service. + * + * @param maxThreads the max number. + */ + @BQConfigProperty("Maximum number of threads that could be used by the executor service") + def setMaxThreads(maxThreads : int) : void { + if (maxThreads > 0) { + this.maxThreads = maxThreads; + } else { + this.maxThreads = 1 + } + } + + /** Replies the maximum number of threads that could be used by the executor service. + * + * @param the max number. + */ + def getMaxThreads : int { + if (this.maxThreads !== null) { + return this.maxThreads.intValue + } + return Integer::MAX_VALUE + } + + /** Replies if the maximum number of threads is specified. + * + * @return {@code true} if the maximum number of threads is specified. Otherwise {@code false}. + */ + def hasMaxThreadsSpecified : boolean { + this.maxThreads !== null + } + + /** Change the minimum number of threads that could be used by the executor service. + * + * @param minThreads the min number. + */ + @BQConfigProperty("Minimum number of threads that could be used by the executor service") + def setMinThreads(minThreads : int) { + if (minThreads > 0) { + this.minThreads = minThreads; + } else { + this.minThreads = 1 + } + } + + /** Change the duration during which a thread is still in memory without being used for another task. + * + * @param duration the duration of an iddle thread. + */ + @BQConfigProperty("Duration during which a thread is iddle before being destroyed") + def setKeepAliveDuration(duration : int) { + if (duration >= 0) { + this.keepAliveDuration = duration; + } else { + this.keepAliveDuration = 0 + } + } + + /** Change the timeout duration for waiting the termination of the threads. + * + * @param duration the timeout duration. + */ + @BQConfigProperty("Duration before assuming a timeout for the threads' termination") + def setTimeout(duration : int) { + if (duration >= 0) { + this.timeout = duration; + } else { + this.timeout = 0 + } + } + + /** + * Change the verbosity level of the internal errors. + * + * @return the verbose level. + */ + @BQConfigProperty("Verbosity level for the internal errors within the executor service.") + def setInternalErrorVerboseLevel(level : Level) { + this.internalErrorVerboseLevel = level + } + + /** + * Replies the verbosity level of the internal errors. + * + * @return the verbose level + */ + def getInternalErrorVerboseLevel : Level { + if (this.internalErrorVerboseLevel === null) { + this.internalErrorVerboseLevel = INTERNAL_ERROR_VERBOSE_LEVEL_VALUE + } + return this.internalErrorVerboseLevel + } + +} + +/** + * Module for the parallel executors' configuration. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class ExecutorsConfigModule extends AbstractModule { + + static val XMXTHREADS_OPTION = "Xmxthreads" + + static val XMNTHREADS_OPTION = "Xmnthreads" + + static val XKEEPALIVE_OPTION = "XkeepAlive" + + static val XTHREADTIMEOUT_OPTION = "XthreadTimeout" + + static val XINTERNALERRORLOGLEVEL_OPTION = "XinternalErrorLogLevel" + + override configure : void { + VariableDecls::extend(binder).declareVar(MAX_NUMBER_OF_THREADS_IN_EXECUTOR_NAME) + binder.extend.addOption( + OptionMetadata::builder(XMXTHREADS_OPTION, Messages::ExecutorsConfigModule_0) + .valueRequired(Messages::ExecutorsConfigModule_5) + .build) + .mapConfigPath(XMXTHREADS_OPTION, MAX_NUMBER_OF_THREADS_IN_EXECUTOR_NAME) + + VariableDecls::extend(binder).declareVar(MIN_NUMBER_OF_THREADS_IN_EXECUTOR_NAME) + binder.extend.addOption(OptionMetadata::builder(XMNTHREADS_OPTION, Messages::ExecutorsConfigModule_1) + .valueRequired(Messages::ExecutorsConfigModule_5) + .build) + .mapConfigPath(XMNTHREADS_OPTION, MIN_NUMBER_OF_THREADS_IN_EXECUTOR_NAME) + + VariableDecls::extend(binder).declareVar(THREAD_KEEP_ALIVE_DURATION_NAME) + binder.extend.addOption(OptionMetadata::builder(XKEEPALIVE_OPTION, + MessageFormat::format(Messages::ExecutorsConfigModule_2, THREAD_KEEP_ALIVE_DURATION_VALUE)) + .valueRequired(Messages::ExecutorsConfigModule_6) + .build) + .mapConfigPath(XKEEPALIVE_OPTION, THREAD_KEEP_ALIVE_DURATION_NAME) + + VariableDecls::extend(binder).declareVar(THREAD_TIMEOUT_NAME) + binder.extend.addOption(OptionMetadata::builder( + XTHREADTIMEOUT_OPTION, + MessageFormat::format(Messages::ExecutorsConfigModule_3, THREAD_TIMEOUT_VALUE)) + .valueRequired(Messages::ExecutorsConfigModule_7) + .build) + .mapConfigPath(XTHREADTIMEOUT_OPTION, THREAD_TIMEOUT_NAME) + + VariableDecls::extend(binder).declareVar(INTERNAL_ERROR_VERBOSE_LEVEL_NAME) + binder.extend.addOption(OptionMetadata::builder( + XINTERNALERRORLOGLEVEL_OPTION, + MessageFormat::format(Messages::ExecutorsConfigModule_4, INTERNAL_ERROR_VERBOSE_LEVEL_VALUE.toString)) + .valueRequired(Messages::ExecutorsConfigModule_8) + .build) + .mapConfigPath(XINTERNALERRORLOGLEVEL_OPTION, INTERNAL_ERROR_VERBOSE_LEVEL_NAME) + } + +} + +/** Provider of the module for the parallel executors' configuration. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class ExecutorsConfigModuleProvider implements BQModuleProvider { + + override module : Module { + return new ExecutorsConfigModule + } + + override configs : Map { + return Collections::singletonMap(PREFIX, typeof(ExecutorsConfig)) + } + + override moduleBuilder : BQModule.Builder { + return BQModule::builder(module).overrides(overrides).providerName(name).configs(configs).description( + Messages::ExecutorsConfigModuleProvider_0); + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/subconfigs/Messages.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/subconfigs/Messages.sarl new file mode 100644 index 0000000000..f35c11d0ec --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/subconfigs/Messages.sarl @@ -0,0 +1,68 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.configs.subconfigs + +import org.eclipse.osgi.util.NLS + +/** Messages for the SARL batch compiler. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @ExcludeFromApidoc + */ +final class Messages extends NLS { + + static val BUNDLE_NAME = typeof(Messages).getPackage.name + ".messages" + + static new { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, typeof(Messages)) + } + + public static var BootConfigModule_0 : String + public static var BootConfigModule_1 : String + public static var BootConfigModule_2 : String + public static var BootConfigModule_3 : String + public static var BootConfigModuleProvider_0 : String + public static var ExecutorsConfigModule_0 : String + public static var ExecutorsConfigModule_1 : String + public static var ExecutorsConfigModule_2 : String + public static var ExecutorsConfigModule_3 : String + public static var ExecutorsConfigModule_4 : String + public static var ExecutorsConfigModule_5 : String + public static var ExecutorsConfigModule_6 : String + public static var ExecutorsConfigModule_7 : String + public static var ExecutorsConfigModule_8 : String + public static var ExecutorsConfigModuleProvider_0 : String + public static var ServicesConfigModule_0 : String + public static var ServicesConfigModule_1 : String + public static var ServicesConfigModule_2 : String + public static var ServicesConfigModule_3 : String + public static var ServicesConfigModule_4 : String + public static var ServicesConfigModuleProvider_0 : String + + private new { + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/subconfigs/RootContextType.java b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/subconfigs/RootContextType.java new file mode 100644 index 0000000000..4aee7c26a5 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/subconfigs/RootContextType.java @@ -0,0 +1,107 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.configs.subconfigs; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.google.common.base.Strings; + +/** + * Type of root context at boot time. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.8.0 + */ +public enum RootContextType { + + /** The identifier is the default one. + */ + DEFAULT, + + /** The identifier is computed randomly. + */ + RANDOM, + + /** The identifier is computed from the name of the boot agent. + */ + BOOT_AGENT_NAME; + + /** Parse the given case insensitive string for obtaining the type. + * + * @param name the string to parse. + * @return the type. + */ + @JsonCreator + public static RootContextType valueOfCaseInsensitive(String name) { + if (Strings.isNullOrEmpty(name)) { + throw new NullPointerException("name is null"); //$NON-NLS-1$ + } + try { + final RootContextType type = valueOf(name.toUpperCase()); + if (type != null) { + return type; + } + } catch (Throwable exception) { + // + } + throw new IllegalArgumentException("illegal value for name: " + name); //$NON-NLS-1$ + } + + /** Replies the Json string representation of this type. + * + * @return the Json string representation. + */ + @JsonValue + public String toJsonString() { + return name().toLowerCase(); + } + + /** Replies the default type of context id computation. + * + * @return the default type. + */ + public static RootContextType getDefault() { + return DEFAULT; + } + + /** Replies the default type of context id computation. + * + * @return the default type. + */ + public static String getJsonLabels() { + final StringBuilder buffer = new StringBuilder(); + boolean first = true; + for (final RootContextType type : values()) { + if (first) { + first = false; + } else { + buffer.append(", "); + } + buffer.append(type.toJsonString()); + } + return buffer.toString(); + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/subconfigs/ServicesConfig.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/subconfigs/ServicesConfig.sarl new file mode 100644 index 0000000000..159751d31c --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/subconfigs/ServicesConfig.sarl @@ -0,0 +1,238 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.configs.subconfigs + +import io.bootique.BQModule +import io.bootique.BQModuleProvider +import io.bootique.annotation.BQConfig +import io.bootique.annotation.BQConfigProperty +import io.bootique.meta.application.OptionMetadata +import io.sarl.sre.boot.configs.SreConfig +import java.lang.reflect.Type +import java.text.MessageFormat +import java.util.Collections +import java.util.Map +import org.arakhne.afc.bootique.variables.VariableDecls +import org.eclipse.xtend.lib.annotations.Accessors + +import static io.sarl.sre.boot.configs.subconfigs.ServicesConfig.* + +import static extension io.bootique.BQCoreModule.extend +import com.google.inject.AbstractModule +import com.google.inject.Module + +/** + * Configuration of the SRE services. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.8 + */ +@BQConfig("Configuration of the services inside the SRE") +class ServicesConfig { + + /** + * Prefix for the configuration entries of the services' configurations. + */ + public static val PREFIX : String = SreConfig::PREFIX + ".services"; // $NON-NLS-1$ + + /** + * Name of the property for the default timeout for services' start in milliseconds. + * + * @see #START_TIMEOUT_VALUE + */ + public static val START_TIMEOUT_NAME = PREFIX + ".startTimeout" + + /** + * The default timeout for services' start in milliseconds. + * + * @see #START_TIMEOUT_NAME + */ + public static val START_TIMEOUT_VALUE = 0.seconds + + /** + * Name of the property for the default timeout for services' stop in milliseconds. + * + * @see #STOP_TIMEOUT_VALUE + */ + public static val STOP_TIMEOUT_NAME = PREFIX + ".stopTimeout" + + /** + * The default timeout for services' stop in milliseconds. + * + * @see #STOP_TIMEOUT_NAME + */ + public static val STOP_TIMEOUT_VALUE = 10.seconds + + /** + * Name of the property for the flag that indicates if the probe manager uses an asynchronous + * update mechanism. + * + * @see #ASYNCHRONOUS_PROBE_UPDATE_VALUE + */ + public static val ASYNCHRONOUS_PROBE_UPDATE_NAME = PREFIX + ".asynchronousProbeUpdate" + + /** + * The default value for the flag that indicates if the probe manager uses an asynchronous + * update mechanism. + * + * @see #ASYNCHRONOUS_PROBE_UPDATE_NAME + */ + public static val ASYNCHRONOUS_PROBE_UPDATE_VALUE = true + + @Accessors(PUBLIC_GETTER) + var startTimeout = START_TIMEOUT_VALUE + + @Accessors(PUBLIC_GETTER) + var stopTimeout = STOP_TIMEOUT_VALUE + + @Accessors(PUBLIC_GETTER) + var asynchronousProbeUpdate = ASYNCHRONOUS_PROBE_UPDATE_VALUE + + var executorsConfig : ExecutorsConfig + + /** Change the flag that enables to select the type of probe manager + * + * @param doAsync is {@code true} if the asynchronous update is prefered. + */ + @BQConfigProperty("Flag that indicates if the probe service must use an asynchronous update mechanism, or" + + " a synchronous update mechanism.") + def setAsynchronousProbeUpdate(doAsync : boolean) { + this.asynchronousProbeUpdate = doAsync + } + + /** Change the timeout for the start of all the services. + * + * @param timeout the new timeout in milliseconds. + */ + @BQConfigProperty("Timeout for the services' start in milliseconds. If the value is lower or equal to " + + "zero, then the SRE will wait for the services' start for ever.") + def setStartTimeout(timeout : long) { + if (timeout < 0l) { + this.startTimeout = 0l + } else { + this.startTimeout = timeout + } + } + + /** Change the timeout for the stop of all the services. + * + * @param timeout the new timeout in milliseconds. + */ + @BQConfigProperty("Timeout for the services' stop in milliseconds. If the value is lower or equal to " + + "zero, then the SRE will wait for the services' start for ever.") + def setStopTimeout(timeout : long) { + if (timeout < 0l) { + this.stopTimeout = 0l + } else { + this.stopTimeout = timeout + } + } + + /** Replies the parallel execution configuration. + * + * @return the parallel execution configuration. + */ + def getExecutors : ExecutorsConfig { + if (this.executorsConfig === null) { + this.executorsConfig = new ExecutorsConfig + } + return this.executorsConfig + } + + /** Change the parallel execution configuration. + * + * @param config the parallel execution configuration. + */ + @BQConfigProperty("Configuration for the SRE services.") + def setExecutors(config : ExecutorsConfig) { + this.executorsConfig = config + } + +} + +/** + * Module for the parallel services' configuration. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class ServicesConfigModule extends AbstractModule { + + static val XSERVICESTARTTIMEOUT_OPTION = "XserviceStartTimeout" + + static val XSERVICESTOPTIMEOUT_OPTION = "XserviceStopTimeout" + + static val XASYNCPROBEUPDATE_OPTION = "XasyncProbeUpdate" + + override configure : void { + VariableDecls::extend(binder).declareVar(START_TIMEOUT_NAME) + binder.extend.addOption( + OptionMetadata::builder(XSERVICESTARTTIMEOUT_OPTION, + MessageFormat::format(Messages::ServicesConfigModule_0, START_TIMEOUT_VALUE)) + .valueRequired(Messages::ServicesConfigModule_2) + .build).mapConfigPath(XSERVICESTARTTIMEOUT_OPTION, START_TIMEOUT_NAME) + + VariableDecls::extend(binder).declareVar(STOP_TIMEOUT_NAME) + binder.extend.addOption(OptionMetadata::builder(XSERVICESTOPTIMEOUT_OPTION, + MessageFormat::format(Messages::ServicesConfigModule_1, STOP_TIMEOUT_VALUE)) + .valueRequired(Messages::ServicesConfigModule_2) + .build).mapConfigPath(XSERVICESTOPTIMEOUT_OPTION, STOP_TIMEOUT_NAME) + + VariableDecls::extend(binder).declareVar(ASYNCHRONOUS_PROBE_UPDATE_NAME) + binder.extend.addOption( + OptionMetadata::builder(XASYNCPROBEUPDATE_OPTION, + MessageFormat::format(Messages::ServicesConfigModule_3, ASYNCHRONOUS_PROBE_UPDATE_NAME)).valueRequired( + Messages::ServicesConfigModule_4).build).mapConfigPath(XASYNCPROBEUPDATE_OPTION, + ASYNCHRONOUS_PROBE_UPDATE_NAME) + } + +} + +/** Provider of the module for the parallel executors' configuration. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class ServicesConfigModuleProvider implements BQModuleProvider { + + override module : Module { + return new ServicesConfigModule + } + + override configs : Map { + return Collections::singletonMap(PREFIX, typeof(ServicesConfig)) + } + + override moduleBuilder : BQModule.Builder { + return BQModule::builder(module).overrides(overrides).providerName(name).configs(configs).description( + Messages::ServicesConfigModuleProvider_0); + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/subconfigs/messages.properties b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/subconfigs/messages.properties new file mode 100644 index 0000000000..3caba4bee4 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/configs/subconfigs/messages.properties @@ -0,0 +1,25 @@ +BootConfigModule_0 = Specify the identifier (UUID) of the root context; Default is {0}. +BootConfigModule_1 = Specify the identifier (UUID) of the default space into the root context; Default is {0}. +BootConfigModule_2 = Specify the name of the program; Default is {0}. +BootConfigModule_3 = Specify the method for selecting the identifier of the default context; The possible \ + values are {1}; Default is {0}. +BootConfigModuleProvider_0 = The configuration for the SRE booting parameters. +ExecutorsConfigModule_0 = Specify the maximal number of threads that could be created by the SRE. +ExecutorsConfigModule_1 = Specify the minimal number of threads that should be created by the SRE. +ExecutorsConfigModule_2 = Specify the time during in seconds during which a thread stays alive even if \ + there is not task to run; Default value is {0}. +ExecutorsConfigModule_3 = Specify the numbers of seconds that the SRE is waiting for thread terminations before timeout. +ExecutorsConfigModule_4 = Specify the logging level for the internal errors within the parallel executors; Default is: {0}. +ExecutorsConfigModule_5 = number +ExecutorsConfigModule_6 = duration +ExecutorsConfigModule_7 = timeout +ExecutorsConfigModule_8 = level +ExecutorsConfigModuleProvider_0 = The configuration for the parallel executors. +ServicesConfigModule_0 = Specify the timeout for waiting a service to be started (in milliseconds); A value equal to zero \ + means that the SRE waits for ever; Default is {0}. +ServicesConfigModule_1 = Specify the timeout for waiting a service to be started (in milliseconds); A value equal to zero \ + means that the SRE waits for ever; Default is {0}. +ServicesConfigModule_2 = timeout +ServicesConfigModule_3 = Specify if the probe service must use an asynchronous update engine, or not; Default is {0}. +ServicesConfigModule_4 = {true|false} +ServicesConfigModuleProvider_0 = The configuration for the SRE services. \ No newline at end of file diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/internal/ListenerNotifierModule.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/internal/ListenerNotifierModule.sarl new file mode 100644 index 0000000000..db9ff24849 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/internal/ListenerNotifierModule.sarl @@ -0,0 +1,67 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.internal.internal + +import com.google.inject.AbstractModule +import com.google.inject.Module +import io.bootique.BQModule +import io.bootique.BQModuleProvider +import io.sarl.sre.internal.ListenerNotifier +import io.sarl.sre.internal.SequenceListenerNotifier +import javax.inject.Singleton + +/** + * Module for configuring the methods for notifying the event listeners. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class ListenerNotifierModule extends AbstractModule { + + protected override configure { + typeof(ListenerNotifier).bind.to(typeof(SequenceListenerNotifier)).in(typeof(Singleton)) + } + +} + +/** Provider of the module for configuring the methods for notifying the event listeners.. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class ListenerNotifierModuleProvider implements BQModuleProvider { + + override module : Module { + return new ListenerNotifierModule + } + + override moduleBuilder : BQModule.Builder { + return BQModule::builder(module).overrides(overrides).providerName(name).configs(configs).description( + Messages::ListenerNotifierModuleProvider_0); + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/internal/LockModule.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/internal/LockModule.sarl new file mode 100644 index 0000000000..1c0445c2cf --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/internal/LockModule.sarl @@ -0,0 +1,69 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.internal.internal + +import com.google.inject.AbstractModule +import com.google.inject.Module +import io.bootique.BQModule +import io.bootique.BQModuleProvider +import java.util.concurrent.locks.Lock +import java.util.concurrent.locks.ReadWriteLock +import java.util.concurrent.locks.ReentrantLock +import java.util.concurrent.locks.ReentrantReadWriteLock + +/** + * Module for configuring the injection of locks. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class LockModule extends AbstractModule { + + protected override configure { + typeof(Lock).bind.to(typeof(ReentrantLock)) + typeof(ReadWriteLock).bind.to(typeof(ReentrantReadWriteLock)) + } + +} + +/** Provider of the module for configuring the injection of locks. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class LockModuleProvider implements BQModuleProvider { + + override module : Module { + return new LockModule + } + + override moduleBuilder : BQModule.Builder { + return BQModule::builder(module).overrides(overrides).providerName(name).configs(configs).description( + Messages::LockModuleProvider_0); + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/internal/Messages.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/internal/Messages.sarl new file mode 100644 index 0000000000..c700a0cca1 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/internal/Messages.sarl @@ -0,0 +1,50 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.internal.internal + +import org.eclipse.osgi.util.NLS + +/** Messages for the SARL batch compiler. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @ExcludeFromApidoc + */ +final class Messages extends NLS { + + static val BUNDLE_NAME = typeof(Messages).getPackage.name + ".messages" + + static new { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, typeof(Messages)) + } + + public static var PlatformEventEmitterModuleProvider_0 : String + public static var ListenerNotifierModuleProvider_0 : String + public static var LockModuleProvider_0 : String + + private new { + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/internal/PlatformEventEmitterModule.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/internal/PlatformEventEmitterModule.sarl new file mode 100644 index 0000000000..634bef1f6d --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/internal/PlatformEventEmitterModule.sarl @@ -0,0 +1,111 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.internal.internal + +import com.google.inject.AbstractModule +import com.google.inject.Provides +import io.sarl.sre.KernelScope +import io.sarl.sre.internal.AgentEventEmitter +import io.sarl.sre.internal.ContextMemberEventEmitter +import io.sarl.sre.internal.SpaceEventEmitterFactory +import io.sarl.sre.internal.SubHolonContextEventEmitterFactory +import io.sarl.sre.services.context.ExternalContextMemberListener +import io.sarl.sre.services.context.InternalContextMembershipListenerFactory +import io.sarl.sre.services.context.SpaceRepositoryListenerFactory +import io.sarl.sre.services.lifecycle.LifecycleServiceListener +import io.sarl.sre.services.logging.LoggingService +import javax.inject.Singleton +import io.bootique.BQModuleProvider +import com.google.inject.Module +import io.bootique.BQModule +import io.sarl.sre.spaces.SpaceParticipantListenerFactory +import io.sarl.sre.internal.SpaceParticipantEventEmitterFactory + +/** + * Module for configuring the methods for firing the specific events that are defined into the SARL API. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class PlatformEventEmitterModule extends AbstractModule { + + protected override configure { + } + + @Provides + @KernelScope + @Singleton + static def provideExternalContextMemberListener(logging : LoggingService) : ExternalContextMemberListener { + new ContextMemberEventEmitter(logging.kernelLogger) + } + + @Provides + @KernelScope + @Singleton + static def provideInternalContextMembershipListenerFactory : InternalContextMembershipListenerFactory { + new SubHolonContextEventEmitterFactory + } + + @Provides + @KernelScope + @Singleton + static def provideSpaceRepositoryListenerFactory : SpaceRepositoryListenerFactory { + new SpaceEventEmitterFactory + } + + @Provides + @KernelScope + @Singleton + static def provideSpaceParticipantListenerFactory : SpaceParticipantListenerFactory { + new SpaceParticipantEventEmitterFactory + } + + @Provides + @KernelScope + @Singleton + static def provideLifecycleServiceListener(logging : LoggingService) : LifecycleServiceListener { + new AgentEventEmitter(logging.kernelLogger) + } + +} + +/** Provider of the module for configuring the methods for firing the specific events that are defined into the SARL API. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class PlatformEventEmitterModuleProvider implements BQModuleProvider { + + override module : Module { + return new PlatformEventEmitterModule + } + + override moduleBuilder : BQModule.Builder { + return BQModule::builder(module).overrides(overrides).providerName(name).configs(configs).description( + Messages::PlatformEventEmitterModuleProvider_0); + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/internal/messages.properties b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/internal/messages.properties new file mode 100644 index 0000000000..2a4064aeb3 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/internal/messages.properties @@ -0,0 +1,3 @@ +PlatformEventEmitterModuleProvider_0 = The module for configuring the firing of the SARL-API's events. +ListenerNotifierModuleProvider_0 = The module for configuring the execution policy for notifying the Java event listeners. +LockModuleProvider_0 = The module for configuring the injection of locks. \ No newline at end of file diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/kernel/KernelModule.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/kernel/KernelModule.sarl new file mode 100644 index 0000000000..0523fc523e --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/kernel/KernelModule.sarl @@ -0,0 +1,109 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.internal.kernel + +import com.google.common.util.concurrent.Service +import com.google.inject.AbstractModule +import com.google.inject.Injector +import com.google.inject.Module +import com.google.inject.Provides +import io.bootique.BQModule +import io.bootique.BQModuleProvider +import io.sarl.sre.KernelScope +import io.sarl.sre.boot.configs.SreConfig +import io.sarl.sre.boot.configs.subconfigs.BootConfig +import io.sarl.sre.boot.internal.services.SreServices +import io.sarl.sre.services.GoogleServiceManager +import io.sarl.sre.services.IServiceManager +import io.sarl.sre.services.context.Context +import io.sarl.sre.services.context.ContextFactory +import java.util.Set +import java.util.UUID +import javax.inject.Named +import javax.inject.Provider +import javax.inject.Singleton + +/** + * Module for configuring the kernel specific components. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class KernelModule extends AbstractModule { + + protected override configure { + } + + /** Replies the kernel instance. + * + * @param injector the current injector. + * @param factory the factory of context for creating the root context. + * @param rootContextID the identifier of the root context. + * @param rootDefaultSpaceId the identifier of the root default space. + * @return the kernel instance. + */ + @Provides + @KernelScope + @Singleton + def provideKernelContext(injector : Injector, factory : ContextFactory, + @Named(BootConfig::ROOT_CONTEXT_ID_NAME) rootContextID : UUID, + @Named(BootConfig::ROOT_DEFAULT_SPACE_ID_NAME) rootDefaultSpaceId : UUID) : Context { + var context = factory.newInstance(rootContextID, rootDefaultSpaceId, null) + injector.injectMembers(context) + return context + } + + /** Replies the manager of the SRE services. + * + * @param services the SRE services. + * @param config the provider of configuration. + * @return the manager of the SRE services. + */ + @Provides + @Singleton + def provideServiceManager(@SreServices services : Set, config : Provider) : IServiceManager { + new GoogleServiceManager(services, config.get) + } + +} + +/** Provider of the module for configuring the kernel specific components. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class KernelModuleProvider implements BQModuleProvider { + + override module : Module { + return new KernelModule + } + + override moduleBuilder : BQModule.Builder { + return BQModule::builder(module).overrides(overrides).providerName(name).configs(configs).description( + Messages::KernelModuleProvider_0); + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/kernel/Messages.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/kernel/Messages.sarl new file mode 100644 index 0000000000..28db811b96 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/kernel/Messages.sarl @@ -0,0 +1,49 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.internal.kernel + +import org.eclipse.osgi.util.NLS + +/** Messages for the SARL batch compiler. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @ExcludeFromApidoc + */ +final class Messages extends NLS { + + static val BUNDLE_NAME = typeof(Messages).getPackage.name + ".messages" + + static new { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, typeof(Messages)) + } + + public static var KernelModuleProvider_0 : String + public static var SarlSpecificationCheckerModuleProvider_0 : String + + private new { + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/kernel/SarlSpecificationCheckerModule.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/kernel/SarlSpecificationCheckerModule.sarl new file mode 100644 index 0000000000..34b5203f3b --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/kernel/SarlSpecificationCheckerModule.sarl @@ -0,0 +1,66 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.internal.kernel + +import com.google.inject.AbstractModule +import com.google.inject.Module +import io.bootique.BQModule +import io.bootique.BQModuleProvider +import io.sarl.sarlspecification.SarlSpecificationChecker +import io.sarl.sarlspecification.StandardSarlSpecificationChecker +import javax.inject.Singleton + +/** + * Module for configuring the SARL specification checker. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class SarlSpecificationCheckerModule extends AbstractModule { + + protected override configure { + typeof(SarlSpecificationChecker).bind.to(typeof(StandardSarlSpecificationChecker)).in(typeof(Singleton)) + } + +} + +/** Provider of the module for configuring the SARL specification checker. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class SarlSpecificationCheckerModuleProvider implements BQModuleProvider { + + override module : Module { + return new SarlSpecificationCheckerModule + } + + override moduleBuilder : BQModule.Builder { + return BQModule::builder(module).overrides(overrides).providerName(name).configs(configs).description( + Messages::SarlSpecificationCheckerModuleProvider_0); + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/kernel/messages.properties b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/kernel/messages.properties new file mode 100644 index 0000000000..09d77a32ad --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/kernel/messages.properties @@ -0,0 +1,2 @@ +KernelModuleProvider_0 = The module for configuring the kernel specific components. +SarlSpecificationCheckerModuleProvider_0 = The module for configuring the SARL specification checker. \ No newline at end of file diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/naming/Messages.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/naming/Messages.sarl new file mode 100644 index 0000000000..cfdfff971f --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/naming/Messages.sarl @@ -0,0 +1,49 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.internal.naming + +import org.eclipse.osgi.util.NLS + +/** Messages for the SARL batch compiler. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @ExcludeFromApidoc + */ +final class Messages extends NLS { + + static val BUNDLE_NAME = typeof(Messages).getPackage.name + ".messages" + + static new { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, typeof(Messages)) + } + + public static var NameParserModuleProvider_0 : String + public static var SchemeNameParserModuleProvider_0 : String + + private new { + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/naming/NameParserModule.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/naming/NameParserModule.sarl new file mode 100644 index 0000000000..53a3b079ae --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/naming/NameParserModule.sarl @@ -0,0 +1,68 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.internal.naming + +import com.google.inject.AbstractModule +import com.google.inject.Module +import io.bootique.BQModule +import io.bootique.BQModuleProvider +import io.sarl.sre.naming.INameParser +import io.sarl.sre.naming.NameParser +import javax.inject.Singleton + +/** + * Module for the base name parser. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class NameParserModule extends AbstractModule { + + protected override configure { + typeof(INameParser).bind.to(typeof(NameParser)).in(typeof(Singleton)) + } + +} + +/** Provider of the module for the base name parser. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class NameParserModuleProvider implements BQModuleProvider { + + override module : Module { + return new NameParserModule + } + + override moduleBuilder : BQModule.Builder { + return BQModule::builder(module).overrides(overrides).providerName(name).configs(configs).description( + Messages::NameParserModuleProvider_0); + } + +} + diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/naming/SchemeNameParserModule.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/naming/SchemeNameParserModule.sarl new file mode 100644 index 0000000000..4e475b0b3d --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/naming/SchemeNameParserModule.sarl @@ -0,0 +1,99 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.internal.naming + +import com.google.inject.AbstractModule +import com.google.inject.BindingAnnotation +import com.google.inject.Module +import io.bootique.BQModule +import io.bootique.BQModuleProvider +import io.sarl.sre.naming.AgentSchemeNameParser +import io.sarl.sre.naming.BehaviorSchemeNameParser +import io.sarl.sre.naming.ContextSchemeNameParser +import io.sarl.sre.naming.ISchemeNameParser +import io.sarl.sre.naming.ServiceSchemeNameParser +import io.sarl.sre.naming.SkillSchemeNameParser +import io.sarl.sre.naming.SpaceSchemeNameParser +import java.lang.annotation.Retention +import java.lang.annotation.Target + +import static extension com.google.inject.multibindings.Multibinder.* + +/** + * Module for the scheme name parsers. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class SchemeNameParserModule extends AbstractModule { + + protected override configure { + var ens = binder.newSetBinder(typeof(ISchemeNameParser), typeof(SchemeNameParsers)) + ens.addBinding.to(typeof(ServiceSchemeNameParser)) + ens.addBinding.to(typeof(ContextSchemeNameParser)) + ens.addBinding.to(typeof(SpaceSchemeNameParser)) + ens.addBinding.to(typeof(AgentSchemeNameParser)) + ens.addBinding.to(typeof(SkillSchemeNameParser)) + ens.addBinding.to(typeof(BehaviorSchemeNameParser)) + } + +} + +/** + * Annotation to mark the set of scheme name parsers. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +@Target(PARAMETER, FIELD) +@Retention(RUNTIME) +@BindingAnnotation +annotation SchemeNameParsers { + // +} + +/** Provider of the module for the scheme name parser. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class SchemeNameParserModuleProvider implements BQModuleProvider { + + override module : Module { + return new SchemeNameParserModule + } + + override moduleBuilder : BQModule.Builder { + return BQModule::builder(module).overrides(overrides).providerName(name).configs(configs).description( + Messages::SchemeNameParserModuleProvider_0); + } + +} + diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/naming/messages.properties b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/naming/messages.properties new file mode 100644 index 0000000000..a16e994b3c --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/naming/messages.properties @@ -0,0 +1,2 @@ +NameParserModuleProvider_0 = The module for the base name parser. +SchemeNameParserModuleProvider_0 = The module for the scheme name parser. diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/AllServicesModule.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/AllServicesModule.sarl new file mode 100644 index 0000000000..1c047bc033 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/AllServicesModule.sarl @@ -0,0 +1,41 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.internal.services + +import com.google.inject.BindingAnnotation +import java.lang.annotation.Retention +import java.lang.annotation.Target + +/** + * Annotation to mark the set of SRE services. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +@Target(PARAMETER, FIELD) +@Retention(RUNTIME) +@BindingAnnotation +annotation SreServices { + // +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/ContextServiceModule.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/ContextServiceModule.sarl new file mode 100644 index 0000000000..a4454b8c1f --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/ContextServiceModule.sarl @@ -0,0 +1,74 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.internal.services + +import com.google.common.util.concurrent.Service +import com.google.inject.AbstractModule +import com.google.inject.Module +import io.bootique.BQModule +import io.bootique.BQModuleProvider +import io.sarl.sre.services.context.ContextService +import io.sarl.sre.services.context.LocalSpaceRepository +import io.sarl.sre.services.context.MemoryBasedContextService +import io.sarl.sre.services.context.SpaceRepository +import javax.inject.Singleton + +import static extension com.google.inject.multibindings.Multibinder.* + +/** + * Module for the context services. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class ContextServiceModule extends AbstractModule { + + protected override configure { + typeof(SpaceRepository).bind.to(typeof(LocalSpaceRepository)) + typeof(ContextService).bind.to(typeof(MemoryBasedContextService)).in(typeof(Singleton)) + + binder.newSetBinder(typeof(Service), typeof(SreServices)).addBinding.to(typeof(ContextService)) + } + +} + +/** Provider of the module for the context services. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class ContextServiceProvider implements BQModuleProvider { + + override module : Module { + return new ContextServiceModule + } + + override moduleBuilder : BQModule.Builder { + return BQModule::builder(module).overrides(overrides).providerName(name).configs(configs).description( + Messages::ContextServiceProvider_0); + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/ExecutorServiceModule.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/ExecutorServiceModule.sarl new file mode 100644 index 0000000000..1f3719b7b2 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/ExecutorServiceModule.sarl @@ -0,0 +1,203 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.internal.services + +import com.google.common.util.concurrent.Service +import com.google.inject.AbstractModule +import com.google.inject.Injector +import com.google.inject.Module +import com.google.inject.Provides +import io.bootique.BQModule +import io.bootique.BQModuleProvider +import io.sarl.sre.boot.configs.subconfigs.ExecutorsConfig +import io.sarl.sre.services.executor.JreExecutorService +import io.sarl.sre.services.executor.QuietThreadExecutorPolicy +import io.sarl.sre.services.executor.VerboseThreadExecutorPolicy +import io.sarl.sre.services.logging.LoggingService +import java.lang.Thread.UncaughtExceptionHandler +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors +import java.util.concurrent.RejectedExecutionHandler +import java.util.concurrent.ScheduledExecutorService +import java.util.concurrent.SynchronousQueue +import java.util.concurrent.ThreadPoolExecutor +import java.util.concurrent.TimeUnit +import javax.inject.Provider +import javax.inject.Singleton +import org.arakhne.afc.bootique.log4j.configs.Log4jIntegrationConfig + +import static extension com.google.inject.multibindings.Multibinder.* + +/** + * Module for the execution services. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class ExecutorServiceModule extends AbstractModule { + + protected override configure { + typeof(io.sarl.sre.services.executor.ExecutorService).bind.to(typeof(JreExecutorService)).in(typeof(Singleton)) + + binder.newSetBinder(typeof(Service), typeof(SreServices)).addBinding.to( + typeof(io.sarl.sre.services.executor.ExecutorService)) + } + + /** + * Construct a {@code VerboseThreadExecutorPolicy}. + * + * @param logService the service for logging. + * @return the policy. + */ + @Provides + @Singleton + def providesVerboseThreadExecutorPolicy(logService : LoggingService) : VerboseThreadExecutorPolicy { + new VerboseThreadExecutorPolicy(logService) + } + + /** + * Construct a {@code QuietThreadExecutorPolicy}. + * + * @return the policy. + */ + @Provides + @Singleton + def provideQuietThreadExecutorPolicy : QuietThreadExecutorPolicy { + new QuietThreadExecutorPolicy + } + + /** + * Construct a handler for tasks that cannot be executed by a ThreadPoolExecutor. + * + * @param loggingConfig accessor to the logging bootique factory. + * @param executorsConfig accessor to the execution bootique factory. + * @param injector the current injector + * @return the handler. + */ + @Provides + @Singleton + def provideRejectedExecutionHandler(loggingConfig : Log4jIntegrationConfig, executorsConfig : ExecutorsConfig, + injector : Injector) : RejectedExecutionHandler { + var level = loggingConfig.level + var ielevel = executorsConfig.internalErrorVerboseLevel + var provider : Provider + if (level.compareTo(ielevel) <= 0) { + provider = injector.getProvider(typeof(VerboseThreadExecutorPolicy)) + } else { + provider = injector.getProvider(typeof(QuietThreadExecutorPolicy)) + } + return provider.get + } + + /** + * Construct a handler for exceptions that are not catched. + * + * @param loggingConfig accessor to the logging bootique factory. + * @param executorsConfig accessor to the execution bootique factory. + * @param injector the current injector + * @return the handler. + */ + @Provides + @Singleton + def providetUncaughtExceptionHandler(loggingConfig : Log4jIntegrationConfig, executorsConfig : ExecutorsConfig, + injector : Injector) : UncaughtExceptionHandler { + var level = loggingConfig.level + var ielevel = executorsConfig.internalErrorVerboseLevel + if (level.compareTo(ielevel) <= 0) { + injector.getProvider(typeof(VerboseThreadExecutorPolicy)).get + } else { + injector.getProvider(typeof(QuietThreadExecutorPolicy)).get + } + } + + /** + * Construct a JVM executor service. + * + * @param executorsConfig accessor to the execution bootique factory. + * @param rejectedExecutionHandler the handler for rejected executions. + * @return the service. + */ + @Provides + @Singleton + def provideExecutorService(executorsConfig : ExecutorsConfig, + rejectedExecutionHandler : RejectedExecutionHandler) : ExecutorService { + var executor : ExecutorService = null + if (executorsConfig.hasMaxThreadsSpecified) { + val minPoolSize = executorsConfig.minThreads + val maxPoolSize = executorsConfig.maxThreads + val keepAliveDuration = executorsConfig.keepAliveDuration + var min = Math::max(0, Math::min(minPoolSize, maxPoolSize)) + var max = Math::max(1, Math::max(minPoolSize, maxPoolSize)) + executor = new ThreadPoolExecutor(min, max, keepAliveDuration, TimeUnit::SECONDS, new SynchronousQueue) + } else { + executor = Executors::newFixedThreadPool(ExecutorsConfig::MAX_NUMBER_OF_THREADS_IN_EXECUTOR_VALUE) + } + if (rejectedExecutionHandler !== null && executor instanceof ThreadPoolExecutor) { + (executor as ThreadPoolExecutor).rejectedExecutionHandler = rejectedExecutionHandler + } + return executor + } + + /** + * Construct a JVM scheduled executor service. + * + * @param executorsConfig accessor to the execution bootique factory. + * @param rejectedExecutionHandler the handler for rejected executions. + * @return the service. + */ + @Provides + @Singleton + def provideJvmScheduledExecutorService(executorsConfig : ExecutorsConfig, + rejectedExecutionHandler : RejectedExecutionHandler) : ScheduledExecutorService { + val minPoolSize = executorsConfig.minThreads + val maxPoolSize = executorsConfig.maxThreads + var max = Math::max(1, Math::min(minPoolSize, maxPoolSize)) + var executor = Executors.newScheduledThreadPool(max) + if (rejectedExecutionHandler !== null && executor instanceof ThreadPoolExecutor) { + (executor as ThreadPoolExecutor).rejectedExecutionHandler = rejectedExecutionHandler + } + return executor + } + +} + +/** Provider of the module for the execution services. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class ExecutorServiceModuleProvider implements BQModuleProvider { + + override module : Module { + return new ExecutorServiceModule + } + + override moduleBuilder : BQModule.Builder { + return BQModule::builder(module).overrides(overrides).providerName(name).configs(configs).description( + Messages::ExecutorServiceModuleProvider_0); + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/InfrastructureServiceModule.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/InfrastructureServiceModule.sarl new file mode 100644 index 0000000000..fa28b674e2 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/InfrastructureServiceModule.sarl @@ -0,0 +1,71 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.internal.services + +import com.google.common.util.concurrent.Service +import com.google.inject.AbstractModule +import com.google.inject.Module +import io.bootique.BQModule +import io.bootique.BQModuleProvider +import io.sarl.sre.services.infrastructure.BasicInfrastructureService +import io.sarl.sre.services.infrastructure.InfrastructureService +import javax.inject.Singleton + +import static extension com.google.inject.multibindings.Multibinder.* + +/** + * Module for the infrastructure services. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class InfrastructureServiceModule extends AbstractModule { + + protected override configure { + typeof(InfrastructureService).bind.to(typeof(BasicInfrastructureService)).in(typeof(Singleton)) + + binder.newSetBinder(typeof(Service), typeof(SreServices)).addBinding.to(typeof(InfrastructureService)) + } + +} + +/** Provider of the module for the infrastructure services. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class InfrastructureServiceModuleProvider implements BQModuleProvider { + + override module : Module { + return new InfrastructureServiceModule + } + + override moduleBuilder : BQModule.Builder { + return BQModule::builder(module).overrides(overrides).providerName(name).configs(configs).description( + Messages::InfrastructureServiceModuleProvider_0); + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/LifecycleServiceModule.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/LifecycleServiceModule.sarl new file mode 100644 index 0000000000..c034536da1 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/LifecycleServiceModule.sarl @@ -0,0 +1,72 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.internal.services + +import com.google.common.util.concurrent.Service +import com.google.inject.AbstractModule +import com.google.inject.Module +import io.bootique.BQModule +import io.bootique.BQModuleProvider +import io.sarl.sre.services.lifecycle.InjectionBasedLifecycleService +import io.sarl.sre.services.lifecycle.LifecycleService +import javax.inject.Singleton + +import static extension com.google.inject.multibindings.Multibinder.* + +/** + * Module for the lifecycle services. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class LifecycleServiceModule extends AbstractModule { + + protected override configure { + typeof(LifecycleService).bind.to(typeof(InjectionBasedLifecycleService)).in(typeof(Singleton)) + + binder.newSetBinder(typeof(Service), typeof(SreServices)).addBinding.to(typeof(LifecycleService)) + } + +} + +/** Provider of the module for the lifecycle services. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class LifecycleServiceModuleProvider implements BQModuleProvider { + + override module : Module { + return new LifecycleServiceModule + } + + override moduleBuilder : BQModule.Builder { + return BQModule::builder(module).overrides(overrides).providerName(name).configs(configs).description( + Messages::LifecycleServiceModuleProvider_0); + } + +} + diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/LoggerCreatorModule.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/LoggerCreatorModule.sarl new file mode 100644 index 0000000000..afbdc418dd --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/LoggerCreatorModule.sarl @@ -0,0 +1,91 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.internal.services + +import com.google.inject.AbstractModule +import com.google.inject.Injector +import com.google.inject.Module +import com.google.inject.Provides +import io.bootique.BQModule +import io.bootique.BQModuleProvider +import io.sarl.sre.boot.configs.subconfigs.BootConfig +import io.sarl.sre.services.logging.JulLoggerCreator +import java.util.logging.Logger +import javax.inject.Provider +import org.arakhne.afc.bootique.log4j.configs.Log4jIntegrationConfig +import javax.inject.Singleton + +/** + * Module for the creator of loggers. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class LoggerCreatorModule extends AbstractModule { + + protected override configure { + } + + /** + * Replies the creator of loggers. + * + * @param loggingFactory accessor to the logging bootique factory. + * @param bootFactory accessor to the boot bootique factory. + * @param injector the injector of the members. + * @return the logger creator. + */ + @Provides + @Singleton + def provideLoggerCreator(loggingConfig : Log4jIntegrationConfig, bootFactory : BootConfig, + injector : Injector, loggerProvider : Provider) : JulLoggerCreator { + var creator = new JulLoggerCreator( + loggingConfig.level.toJul, + bootFactory.programName, + loggerProvider) + injector.injectMembers(creator) + return creator + } + +} + +/** Provider of the module for the logging services. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class LoggerCreatorModuleProvider implements BQModuleProvider { + + override module : Module { + return new LoggerCreatorModule + } + + override moduleBuilder : BQModule.Builder { + return BQModule::builder(module).overrides(overrides).providerName(name).configs(configs).description( + Messages::LoggerCreatorModuleProvider_0); + } + +} + diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/LoggingServiceModule.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/LoggingServiceModule.sarl new file mode 100644 index 0000000000..1fe41ae3b2 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/LoggingServiceModule.sarl @@ -0,0 +1,97 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.internal.services + +import com.google.common.util.concurrent.Service +import com.google.inject.AbstractModule +import com.google.inject.Injector +import com.google.inject.Module +import com.google.inject.Provides +import io.bootique.BQModule +import io.bootique.BQModuleProvider +import io.sarl.sre.services.logging.JulLoggingService +import io.sarl.sre.services.logging.LoggingService +import io.sarl.sre.services.logging.QuietLoggingService +import org.arakhne.afc.bootique.log4j.configs.Level +import org.arakhne.afc.bootique.log4j.configs.Log4jIntegrationConfig + +import static extension com.google.inject.multibindings.Multibinder.* +import javax.inject.Singleton +import io.sarl.sre.services.logging.JulLoggerCreator + +/** + * Module for the logging services. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class LoggingServiceModule extends AbstractModule { + + protected override configure { + binder.newSetBinder(typeof(Service), typeof(SreServices)).addBinding.to(typeof(LoggingService)) + } + + /** + * Replies the verbose level. + * + * @param configFactory accessor to the bootique factory. + * @param injector the injector of the members. + * @return the verbose level. + */ + @Provides + @Singleton + def provideLoggingService(loggingConfig : Log4jIntegrationConfig, injector : Injector) : LoggingService { + var level = loggingConfig.level + var srv : LoggingService + if (level != Level::OFF) { + srv = new JulLoggingService(injector.getInstance(typeof(JulLoggerCreator))) + } else { + srv = new QuietLoggingService + } + injector.injectMembers(srv) + return srv + } + +} + +/** Provider of the module for the logging services. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class LoggingServiceModuleProvider implements BQModuleProvider { + + override module : Module { + return new LoggingServiceModule + } + + override moduleBuilder : BQModule.Builder { + return BQModule::builder(module).overrides(overrides).providerName(name).configs(configs).description( + Messages::LoggingServiceModuleProvider_0); + } + +} + diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/Messages.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/Messages.sarl new file mode 100644 index 0000000000..fac067d540 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/Messages.sarl @@ -0,0 +1,57 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.internal.services + +import org.eclipse.osgi.util.NLS + +/** Messages for the SARL batch compiler. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @ExcludeFromApidoc + */ +final class Messages extends NLS { + + static val BUNDLE_NAME = typeof(Messages).getPackage.name + ".messages" + + static new { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, typeof(Messages)) + } + + public static var ContextServiceProvider_0 : String + public static var ExecutorServiceModuleProvider_0 : String + public static var InfrastructureServiceModuleProvider_0 : String + public static var LifecycleServiceModuleProvider_0 : String + public static var LoggerCreatorModuleProvider_0 : String + public static var LoggingServiceModuleProvider_0 : String + public static var TimeServiceModuleProvider_0 : String + public static var NamespaceServiceModuleProvider_0 : String + public static var NamespaceFinderModuleProvider_0 : String + public static var ProbeServiceModuleProvider_0 : String + + private new { + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/NamespaceFinderModule.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/NamespaceFinderModule.sarl new file mode 100644 index 0000000000..8b76362b61 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/NamespaceFinderModule.sarl @@ -0,0 +1,99 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.internal.services + +import com.google.inject.AbstractModule +import com.google.inject.BindingAnnotation +import com.google.inject.Module +import io.bootique.BQModule +import io.bootique.BQModuleProvider +import io.sarl.sre.services.namespace.AgentNamespaceFinder +import io.sarl.sre.services.namespace.BehaviorNamespaceFinder +import io.sarl.sre.services.namespace.ContextNamespaceFinder +import io.sarl.sre.services.namespace.INamespaceFinder +import io.sarl.sre.services.namespace.ServiceNamespaceFinder +import io.sarl.sre.services.namespace.SkillNamespaceFinder +import io.sarl.sre.services.namespace.SpaceNamespaceFinder +import java.lang.annotation.Retention +import java.lang.annotation.Target + +import static extension com.google.inject.multibindings.Multibinder.* + +/** + * Module for the namespace finders. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class NamespaceFinderModule extends AbstractModule { + + protected override configure { + var ens = binder.newSetBinder(typeof(INamespaceFinder), typeof(NamespaceFinders)) + ens.addBinding.to(typeof(ServiceNamespaceFinder)) + ens.addBinding.to(typeof(ContextNamespaceFinder)) + ens.addBinding.to(typeof(SpaceNamespaceFinder)) + ens.addBinding.to(typeof(AgentNamespaceFinder)) + ens.addBinding.to(typeof(SkillNamespaceFinder)) + ens.addBinding.to(typeof(BehaviorNamespaceFinder)) + } + +} + +/** + * Annotation to mark the set of namespace finders. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +@Target(PARAMETER, FIELD) +@Retention(RUNTIME) +@BindingAnnotation +annotation NamespaceFinders { + // +} + +/** Provider of the module for the namespace finders. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class NamespaceFinderModuleProvider implements BQModuleProvider { + + override module : Module { + return new NamespaceFinderModule + } + + override moduleBuilder : BQModule.Builder { + return BQModule::builder(module).overrides(overrides).providerName(name).configs(configs).description( + Messages::NamespaceFinderModuleProvider_0) + } + +} + diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/NamespaceServiceModule.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/NamespaceServiceModule.sarl new file mode 100644 index 0000000000..939a4351e1 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/NamespaceServiceModule.sarl @@ -0,0 +1,73 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.internal.services + +import com.google.common.util.concurrent.Service +import com.google.inject.AbstractModule +import com.google.inject.Module +import io.bootique.BQModule +import io.bootique.BQModuleProvider +import io.sarl.sre.services.namespace.FinderBasedNamespaceService +import io.sarl.sre.services.namespace.NamespaceService +import javax.inject.Singleton + +import static extension com.google.inject.multibindings.Multibinder.* + +/** + * Module for the namespace services. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class NamespaceServiceModule extends AbstractModule { + + protected override configure { + typeof(NamespaceService).bind.to(typeof(FinderBasedNamespaceService)).in(typeof(Singleton)) + + binder.newSetBinder(typeof(Service), typeof(SreServices)).addBinding.to(typeof(NamespaceService)) + } + +} + +/** Provider of the module for the namespace services. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class NamespaceServiceModuleProvider implements BQModuleProvider { + + override module : Module { + return new NamespaceServiceModule + } + + override moduleBuilder : BQModule.Builder { + return BQModule::builder(module).overrides(overrides).providerName(name).configs(configs).description( + Messages::NamespaceServiceModuleProvider_0) + } + +} + diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/ProbeServiceModule.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/ProbeServiceModule.sarl new file mode 100644 index 0000000000..71e93b07b8 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/ProbeServiceModule.sarl @@ -0,0 +1,106 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.internal.services + +import com.google.common.util.concurrent.Service +import com.google.inject.AbstractModule +import com.google.inject.Injector +import com.google.inject.Module +import com.google.inject.Provides +import io.bootique.BQModule +import io.bootique.BQModuleProvider +import io.sarl.sre.boot.configs.SreConfig +import io.sarl.sre.internal.SmartListenerCollection +import io.sarl.sre.naming.NameParser +import io.sarl.sre.services.executor.ExecutorService +import io.sarl.sre.services.namespace.NamespaceService +import io.sarl.sre.services.probing.AsynchronousProbeService +import io.sarl.sre.services.probing.ProbeService +import io.sarl.sre.services.probing.SynchronousProbeService +import java.util.concurrent.locks.ReadWriteLock +import javax.inject.Provider +import javax.inject.Singleton + +import static extension com.google.inject.multibindings.Multibinder.* + +/** + * Module for the probing services. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class ProbeServiceModule extends AbstractModule { + + protected override configure { + binder.newSetBinder(typeof(Service), typeof(SreServices)).addBinding.to(typeof(ProbeService)) + } + + /** Create an instance of the probe service. + * + * @param sreConfig the SRE configuration. + * @param injector the injector to use for initializing the new instance. + * @param listenerCollectionProvider the provider of listener collections. + * @param lockProvider the provider of synchronization locks. + * @return the created service. + */ + @Singleton + @Provides + def providesProbeService(sreConfig : SreConfig, injector : Injector, service : ExecutorService, + namespaceService : NamespaceService, nameParser : NameParser, + listenerCollectionProvider : Provider>, + lockProvider : Provider) : ProbeService { + var ps : ProbeService + if (sreConfig.services.asynchronousProbeUpdate) { + ps = new AsynchronousProbeService(service, namespaceService, nameParser, listenerCollectionProvider, lockProvider) + + + } else { + ps = new SynchronousProbeService(namespaceService, nameParser, listenerCollectionProvider, lockProvider) + } + injector.injectMembers(ps) + return ps + } + +} + +/** Provider of the module for the probing services. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class ProbeServiceModuleProvider implements BQModuleProvider { + + override module : Module { + return new ProbeServiceModule + } + + override moduleBuilder : BQModule.Builder { + return BQModule::builder(module).overrides(overrides).providerName(name).configs(configs).description( + Messages::ProbeServiceModuleProvider_0); + } + +} + diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/TimeServiceModule.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/TimeServiceModule.sarl new file mode 100644 index 0000000000..922778b0c0 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/TimeServiceModule.sarl @@ -0,0 +1,71 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.internal.services + +import com.google.common.util.concurrent.Service +import com.google.inject.AbstractModule +import com.google.inject.Module +import io.bootique.BQModule +import io.bootique.BQModuleProvider +import io.sarl.sre.services.time.JreTimeService +import io.sarl.sre.services.time.TimeService +import javax.inject.Singleton + +import static extension com.google.inject.multibindings.Multibinder.* + +/** + * Module for the time services that are based on the operating system time. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class TimeServiceModule extends AbstractModule { + + protected override configure { + typeof(TimeService).bind.to(typeof(JreTimeService)).in(typeof(Singleton)) + + binder.newSetBinder(typeof(Service), typeof(SreServices)).addBinding.to(typeof(TimeService)) + } + +} + +/** Provider of the module for the time services. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class TimeServiceModuleProvider implements BQModuleProvider { + + override module : Module { + return new TimeServiceModule + } + + override moduleBuilder : BQModule.Builder { + return BQModule::builder(module).overrides(overrides).providerName(name).configs(configs).description( + Messages::TimeServiceModuleProvider_0); + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/messages.properties b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/messages.properties new file mode 100644 index 0000000000..c4d7da77f3 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/services/messages.properties @@ -0,0 +1,10 @@ +ContextServiceProvider_0 = The module for the context service. +ExecutorServiceModuleProvider_0 = The module for the execution service. +InfrastructureServiceModuleProvider_0 = The module for the infrastructure service. +LifecycleServiceModuleProvider_0 = The module for the life-cycle service. +LoggerCreatorModuleProvider_0 = The module for the logger creator. +LoggingServiceModuleProvider_0 = The module for the logging service. +TimeServiceModuleProvider_0 = The module for the time service. +NamespaceServiceModuleProvider_0 = The module for the name space service. +NamespaceFinderModuleProvider_0 = The module for the name space finders. +ProbeServiceModuleProvider_0 = The module for the probe service. diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/skills/BuiltinCapacityModule.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/skills/BuiltinCapacityModule.sarl new file mode 100644 index 0000000000..7ffd0958ab --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/skills/BuiltinCapacityModule.sarl @@ -0,0 +1,66 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.internal.skills + +import com.google.inject.AbstractModule +import io.sarl.sre.skills.SreDynamicSkillProvider +import io.sarl.lang.core.DynamicSkillProvider +import javax.inject.Singleton +import io.bootique.BQModuleProvider +import com.google.inject.Module +import io.bootique.BQModule + +/** + * Module for configuring the built-in capacities. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class BuiltinCapacityModule extends AbstractModule { + + protected override configure { + typeof(DynamicSkillProvider).bind.to(typeof(SreDynamicSkillProvider)).in(typeof(Singleton)) + } + +} + +/** Provider of the module for configuring the built-in capacities. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class BuiltinCapacityModuleProvider implements BQModuleProvider { + + override module : Module { + return new BuiltinCapacityModule + } + + override moduleBuilder : BQModule.Builder { + return BQModule::builder(module).overrides(overrides).providerName(name).configs(configs).description( + Messages::BuiltinCapacityModuleProvider_0); + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/skills/EventBusModule.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/skills/EventBusModule.sarl new file mode 100644 index 0000000000..39a2e1605e --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/skills/EventBusModule.sarl @@ -0,0 +1,79 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.internal.skills + +import com.google.inject.AbstractModule +import com.google.inject.Injector +import com.google.inject.Provides +import io.sarl.sre.services.executor.ExecutorService +import io.sarl.sre.skills.internal.EventBus +import javax.inject.Provider +import io.bootique.BQModuleProvider +import com.google.inject.Module +import io.bootique.BQModule +import java.util.concurrent.locks.ReadWriteLock + +/** + * Module for configuring the agents' event buses. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class EventBusModule extends AbstractModule { + + protected override configure { + // + } + + @Provides + def provideEventBus(executorService : Provider, lockProvider : Provider, injector : Injector) : EventBus { + // Ensure a new instance is created at each injection. + val aeb = new EventBus(executorService.get, lockProvider) + // to be able to inject the ExecutorService and SubscriberFindingStrategy + injector.injectMembers(aeb) + return aeb + } + +} + +/** Provider of the module for configuring the agents' event buses. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class EventBusModuleProvider implements BQModuleProvider { + + override module : Module { + return new EventBusModule + } + + override moduleBuilder : BQModule.Builder { + return BQModule::builder(module).overrides(overrides).providerName(name).configs(configs).description( + Messages::EventBusModuleProvider_0); + } + +} + diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/skills/Messages.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/skills/Messages.sarl new file mode 100644 index 0000000000..28dba7cf0d --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/skills/Messages.sarl @@ -0,0 +1,49 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.internal.skills + +import org.eclipse.osgi.util.NLS + +/** Messages for the SARL batch compiler. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @ExcludeFromApidoc + */ +final class Messages extends NLS { + + static val BUNDLE_NAME = typeof(Messages).getPackage.name + ".messages" + + static new { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, typeof(Messages)) + } + + public static var BuiltinCapacityModuleProvider_0 : String + public static var EventBusModuleProvider_0 : String + + private new { + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/skills/messages.properties b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/skills/messages.properties new file mode 100644 index 0000000000..017b040a87 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/skills/messages.properties @@ -0,0 +1,2 @@ +BuiltinCapacityModuleProvider_0 = The module for configuring the built-in capacities. +EventBusModuleProvider_0 = The module for configuring the agents' event buses. \ No newline at end of file diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/spaces/Messages.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/spaces/Messages.sarl new file mode 100644 index 0000000000..4b87a99e55 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/spaces/Messages.sarl @@ -0,0 +1,48 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.internal.spaces + +import org.eclipse.osgi.util.NLS + +/** Messages for the SARL batch compiler. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @ExcludeFromApidoc + */ +final class Messages extends NLS { + + static val BUNDLE_NAME = typeof(Messages).getPackage.name + ".messages" + + static new { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, typeof(Messages)) + } + + public static var SpacesModuleProvider_0 : String + + private new { + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/spaces/SpacesModule.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/spaces/SpacesModule.sarl new file mode 100644 index 0000000000..b63a25ecb4 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/spaces/SpacesModule.sarl @@ -0,0 +1,82 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.boot.internal.spaces + +import com.google.inject.AbstractModule +import com.google.inject.Injector +import com.google.inject.Module +import com.google.inject.Provides +import io.bootique.BQModule +import io.bootique.BQModuleProvider +import io.sarl.core.OpenEventSpaceSpecification +import io.sarl.sre.KernelScope +import io.sarl.sre.services.logging.LoggingService +import io.sarl.sre.spaces.SpaceParticipantListenerFactory +import io.sarl.sre.spaces.SreOpenEventSpaceSpecification +import java.util.concurrent.locks.ReadWriteLock +import javax.inject.Provider + +/** + * Module for configuring the SARL spaces. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class SpacesModule extends AbstractModule { + + protected override configure { + // + } + + /** This injection definition is for the default spaces. */ + @Provides + @KernelScope + def provideDefaultSpaceSpecification(injector : Injector, lockProvider : Provider, + @KernelScope spaceParticipantListenerFactory : Provider, + logger : LoggingService) : OpenEventSpaceSpecification { + new SreOpenEventSpaceSpecification(injector, lockProvider, null, spaceParticipantListenerFactory.get, + logger) + } + +} + +/** Provider of the module for configuring the SARL spaces. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ +class SpacesModuleProvider implements BQModuleProvider { + + override module : Module { + return new SpacesModule + } + + override moduleBuilder : BQModule.Builder { + return BQModule::builder(module).overrides(overrides).providerName(name).configs(configs).description( + Messages::SpacesModuleProvider_0); + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/spaces/messages.properties b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/spaces/messages.properties new file mode 100644 index 0000000000..82419e6983 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/boot/internal/spaces/messages.properties @@ -0,0 +1 @@ +SpacesModuleProvider_0 = The module for configuring the SARL spaces. \ No newline at end of file diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/capacities/InformedEventListener.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/capacities/InformedEventListener.sarl new file mode 100644 index 0000000000..9804ec80ad --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/capacities/InformedEventListener.sarl @@ -0,0 +1,57 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.capacities + +import io.sarl.lang.annotation.PrivateAPI +import io.sarl.lang.core.Agent +import io.sarl.lang.core.EventListener +import io.sarl.lang.core.EventSpace +import io.sarl.lang.core.Space + +/** + * Entity capable listening to events inside an Interaction {@link Space}. + * + *

{@link Space}s in SARL are event driven. Most of the time, + * {@link EventListener}s in a {@link Space} will be {@link Agent}s. However, + * any entity implementing the {@link EventListener} can interact inside an + * {@link EventSpace} (e.g. UI) + * + *

This specific listening entity knows its owner and provides access to it. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +interface InformedEventListener extends EventListener { + + /** Replies the owner of this listener. + *

This function is part of the private API and should not be invoked + * outside the implementation of the SRE. + * + * @return the agent instance. + */ + @SuppressWarnings("use_reserved_sarl_annotation") + @PrivateAPI + def getOwnerInstance : Agent + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/capacities/InternalEventBus.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/capacities/InternalEventBus.sarl new file mode 100644 index 0000000000..f6392e908b --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/capacities/InternalEventBus.sarl @@ -0,0 +1,156 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.capacities + +import io.sarl.lang.core.Event +import io.sarl.lang.util.SynchronizedIterable +import io.sarl.sre.services.lifecycle.AgentState +import io.sarl.sre.skills.internal.EventBus + +/** + * Capacity that provides an event bus to notify the different components of an agent. + * + *

This capacity is provided by the SRE kernel, not SARL. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +capacity InternalEventBusCapacity { + + /** + * Register the given object on the event bus for receiving any event. + * + *

If the filter is provided, it will be used for determining if the given behavior accepts a specific event. + * If the filter function replies {@code true} for a specific event as argument, the event is fired in the + * behavior context. If the filter function replies {@code false}, the event is not fired in the behavior context. + * + * @param listener the listener on the SARL events. + * @param filter the filter function. + * @param callback function which is invoked just after the first registration of the object. It could be {@code null}. + * @since 0.5.0 + */ + def registerEventBusListener(listener : Object, filter : (Event)=>boolean = null, callback : (Object) => void = null) + + /** + * Unregister the given object on the event bus for receiving any event. + * + * @param listener the listener on the SARL events. + * @param callback function which is invoked just before the object is unregistered. + * @since 0.5.0 + */ + def unregisterEventBusListener(listener : Object, callback : (Object)=>void = null) + + /** + * Unregister the listeners of the given type on the event bus for receiving any event. + * + * @param listenerType the type of the listeners on the SARL events. + * @param callback function which is invoked just before the object is unregistered. + * @since 0.7.0 + */ + def unregisterEventBusListener(listenerType : Class, callback : (Object)=>void = null) + + /** Fire the given event into the agent context, and wait for the execution of all the event handlers. + * + *

This function does not set the event's source if it is not already set. + * + *

All the exceptions thrown by the event handlers are gathered and fired into a single exception after + * all the event handlers have been executed or failed. + * + *

This function does not considered the agent's state for determining of the event should be fired. + * In other words, the given event is always fired, whatever the agent's state. + * + * @param event the event to fire. + * @param gatherEvents indicates if the events that are received during the execution of this function are gathered and replied. + * @param thrownExceptions indicates if any exception, which occurs into the event handler should be thrown outside this function. + * If {@code true}, an exception is thrown outside and should be catch. If {@code false}, the exceptions are logging out + * to the agent's logger, and never thrown outside this function. + * @param listener the sole receiver of the event. If {@code null}, all the listeners on the event bus will + * receive the event. If not {@code null}, only the given listener will receive the event. + * @return the events that were received during the execution of this function. These events are not fired into the agent context yet. + * @since 0.7.0 + */ + def fireEventAndWait(^event : Event, gatherEvents : boolean, thrownExceptions : boolean, + listener : Object) : Iterable + + /** Fire the given event into the agent context, and wait for the execution of all the event handlers. + * + *

This function does not set the event's source if it is not already set. + * + *

All the exceptions thrown by the event handlers are gathered and fired into a single exception after + * all the event handlers have been executed or failed. + * + *

This function does not considered the agent's state for determining of the event should be fired. + * In other words, the given event is always fired, whatever the agent's state. + * + * @param event the event to fire. + * @param gatherEvents indicates if the events that are received during the execution of this function are gathered and replied. + * @param thrownExceptions indicates if any exception, which occurs into the event handler should be thrown outside this function. + * If {@code true}, an exception is thrown outside and should be catch. If {@code false}, the exceptions are logging out + * to the agent's logger, and never thrown outside this function. + * @return the events that were received during the execution of this function. These events are not fired into the agent context yet. + */ + def fireEventAndWait(^event : Event, gatherEvents : boolean, thrownExceptions : boolean) : Iterable + + /** Fire the given event into the agent context, and do not wait for the termination of the event handlers. + * + *

This function does not set the event's source if it is not already set. + * + *

Any exception thrown by an event handler is catch by the associated thread. + * + *

The event is not fired if the agent's state does not allow it. + * See {@link AgentState}. + * + * @param event the event to fire. + */ + def fireEvent(^event : Event) + + /** Replies the event listener linked to the owner of this capacity. + * + * @return the event listener of the owner of this skill. + */ + def getAssociatedEventBusListener : InformedEventListener + + /** + * Replies the registered listeners. + * + * @param the type of the listeners. + * @param type the type of the listeners. + * @return the registered listeners. + */ + def getRegisteredEventBusListeners(type : Class) : SynchronizedIterable with T + + /** Replies the backend event bus. + * + * @return the event bus instance. + */ + def getEventBus : EventBus + + /** Replies the backend event bus. + * + * @param type the expected type of the event bus. + * @return the event bus instance. + * @since 0.10 + */ + def getEventBus(type : Class) : T with T extends EventBus + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/capacities/InternalSchedules.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/capacities/InternalSchedules.sarl new file mode 100644 index 0000000000..dc6a355df3 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/capacities/InternalSchedules.sarl @@ -0,0 +1,49 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.capacities + +import io.sarl.core.Schedules +import io.sarl.lang.core.Behavior + +/** + * Capacity for executing tasks with specific functions for the SRE platform. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.6.0 + */ +capacity InternalSchedules extends Schedules { + + /** Finish the tasks associated to the given behavior. + * + * @param behavior the behavior. + */ + def unregisterTasksForBehavior(^behavior : Behavior) + + /** Release any resource that is dedicated to the behavior itself. + * + * @param behavior the behavior. + */ + def releaseInternalResources(^behavior : Behavior) + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/capacities/MicroKernel.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/capacities/MicroKernel.sarl new file mode 100644 index 0000000000..af4b317525 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/capacities/MicroKernel.sarl @@ -0,0 +1,44 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.capacities + +import com.google.common.util.concurrent.Service + +/** + * Capacity that provides an access to the micro kernel. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +capacity MicroKernelCapacity { + + /** + * Replies a kernel service that is alive. + * + * @param - type of the service to reply. + * @param type type of the service to reply. + * @return the service, or null. + */ + def getService(type : Class) : S with S extends Service + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/internal/ListenerNotifier.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/internal/ListenerNotifier.sarl new file mode 100644 index 0000000000..3de897bbc0 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/internal/ListenerNotifier.sarl @@ -0,0 +1,79 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.internal + +import java.util.Arrays +import java.util.EventListener +import java.util.function.Consumer + +/** This interface represents an object that is able to provide a notification policy + * for calling Java event listeners. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +interface ListenerNotifier { + + /** Build a call policy for the given list of listeners and call them. + * + * @param the type of event listener to support. + * @param listeners is the list of listeners to call. + * @param action is the action to apply on each listener. + */ + def notifyListeners(listeners : L[], action : Consumer) with L extends EventListener + +} + +/** This class implements a parallel execution mechanism for notifying the event listeners. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class ParallelListenerNotifier implements ListenerNotifier { + + override notifyListeners(listeners : L[], action : Consumer) with L extends EventListener { + Arrays::asList(listeners).parallelStream.forEach(action) + } + +} + +/** This class implements a sequence execution mechanism for notifying the event listeners. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class SequenceListenerNotifier implements ListenerNotifier { + + override notifyListeners(listeners : L[], action : Consumer) with L extends EventListener { + Arrays::asList(listeners).stream.forEach(action) + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/internal/Messages.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/internal/Messages.sarl new file mode 100644 index 0000000000..8a51879587 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/internal/Messages.sarl @@ -0,0 +1,59 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.internal + +import org.eclipse.osgi.util.NLS + +/** Messages. + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +final class Messages extends NLS { + + static val BUNDLE_NAME = typeof(Messages).getPackage.name + ".messages" + + static new { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, typeof(Messages)) + } + + public static var SpaceEventEmitter_0 : String + public static var SpaceEventEmitter_1 : String + + public static var SpaceParticipantEventEmitter_0 : String + public static var SpaceParticipantEventEmitter_1 : String + + public static var AgentEventEmitter_0 : String + public static var AgentEventEmitter_1 : String + + public static var ContextMemberEventEmitter_0 : String + public static var ContextMemberEventEmitter_1 : String + public static var ContextMemberEventEmitter_2 : String + public static var ContextMemberEventEmitter_3 : String + + + private new { + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/internal/PlatformEventEmitters.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/internal/PlatformEventEmitters.sarl new file mode 100644 index 0000000000..22f5536358 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/internal/PlatformEventEmitters.sarl @@ -0,0 +1,377 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.internal + +import io.sarl.core.AgentKilled +import io.sarl.core.AgentSpawned +import io.sarl.core.Behaviors +import io.sarl.core.ContextJoined +import io.sarl.core.ContextLeft +import io.sarl.core.Logging +import io.sarl.core.MemberJoined +import io.sarl.core.MemberLeft +import io.sarl.core.SpaceCreated +import io.sarl.core.SpaceDestroyed +import io.sarl.lang.annotation.PrivateAPI +import io.sarl.lang.core.Address +import io.sarl.lang.core.Agent +import io.sarl.lang.core.AgentContext +import io.sarl.lang.core.EventSpace +import io.sarl.lang.core.SREutils +import io.sarl.lang.core.Skill +import io.sarl.lang.core.Space +import io.sarl.lang.core.SpaceID +import io.sarl.lang.util.ClearableReference +import io.sarl.sre.services.context.Context +import io.sarl.sre.services.context.ExternalContextMemberListener +import io.sarl.sre.services.context.InternalContextMembershipListener +import io.sarl.sre.services.context.InternalContextMembershipListenerFactory +import io.sarl.sre.services.context.SpaceRepositoryListener +import io.sarl.sre.services.context.SpaceRepositoryListenerFactory +import io.sarl.sre.services.lifecycle.ContextReference +import io.sarl.sre.services.lifecycle.LifecycleServiceListener +import java.lang.ref.WeakReference +import java.text.MessageFormat +import java.util.List +import java.util.UUID +import java.util.concurrent.locks.ReadWriteLock +import java.util.logging.Logger +import javax.inject.Provider + +import static io.sarl.sre.services.lifecycle.AgentLife.* +import io.sarl.sre.spaces.SpaceParticipantListener +import io.sarl.sre.spaces.Participant +import io.sarl.core.ParticipantJoined +import io.sarl.core.ParticipantLeft +import io.sarl.sre.spaces.SpaceParticipantListenerFactory + +/** Emit the space platform events. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class SpaceEventEmitter implements SpaceRepositoryListener { + + val logger : Logger + + val defaultSpace : EventSpace + + new (contextID : UUID, defaultSpace : EventSpace, logger : Logger) { + this.defaultSpace = defaultSpace + this.logger = logger + } + + override spaceCreated(^space : Space, isLocalCreation : boolean) { + this.logger.config [MessageFormat::format(Messages::SpaceEventEmitter_0, ^space.spaceID)] + // Send the event in the default space of the context. + if (isLocalCreation && this.defaultSpace !== null) { + var spaceId = this.defaultSpace.spaceID + var addr = new Address(spaceId, spaceId.contextID) + // The first parameter is null because the event has an address. + this.defaultSpace.emit(null, new SpaceCreated(addr, ^space.spaceID)) + } + } + + override spaceDestroyed(^space : Space, isLocalDestruction : boolean) { + this.logger.config[MessageFormat::format(Messages::SpaceEventEmitter_1, ^space.spaceID)] + // Send the event in the default space of the context. + if (isLocalDestruction && this.defaultSpace !== null) { + var spaceId = this.defaultSpace.spaceID + var addr = new Address(spaceId, spaceId.contextID) + // The first parameter is null because the event has an address. + this.defaultSpace.emit(null, new SpaceDestroyed(addr, ^space.spaceID)) + } + } + +} + +/** Factory for a space event emitter. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.7.0 + */ +class SpaceEventEmitterFactory implements SpaceRepositoryListenerFactory { + + def create(contextID : UUID, defaultSpace : EventSpace, logger : Logger) : SpaceRepositoryListener { + new SpaceEventEmitter(contextID, defaultSpace, logger) + } + +} + +/** Emit the space participant platform events. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class SpaceParticipantEventEmitter implements SpaceParticipantListener { + + val logger : Logger + + val defaultSpace : EventSpace + + new (defaultSpace : EventSpace, logger : Logger) { + this.defaultSpace = defaultSpace + this.logger = logger + } + + override participantJoined(participant : Participant) { + this.logger.config[MessageFormat::format(Messages::SpaceParticipantEventEmitter_0, participant.address)] + // Send the event in the default space of the context. + if (this.defaultSpace !== null) { + var participantAddr = participant.address + var addr = new Address(this.defaultSpace.spaceID, participantAddr.UUID) + // The first parameter is null because the event has an address. + this.defaultSpace.emit(null, new ParticipantJoined(addr, participantAddr.spaceID)) + } + } + + override participantLeft(participant : Participant) { + this.logger.config[MessageFormat::format(Messages::SpaceParticipantEventEmitter_1, participant.address)] + // Send the event in the default space of the context. + if (this.defaultSpace !== null) { + var participantAddr = participant.address + var addr = new Address(this.defaultSpace.spaceID, participantAddr.UUID) + // The first parameter is null because the event has an address. + this.defaultSpace.emit(null, new ParticipantLeft(addr, participantAddr.spaceID)) + } + } + +} + +/** Factory for a space participant event emitter. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class SpaceParticipantEventEmitterFactory implements SpaceParticipantListenerFactory { + + def create(defaultSpace : EventSpace, logger : Logger) : SpaceParticipantListener { + new SpaceParticipantEventEmitter(defaultSpace, logger) + } + +} + + +/** Emit the agent platform events. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class AgentEventEmitter implements LifecycleServiceListener { + + val logger : Logger + + new (logger : Logger) { + this.logger = logger + } + + override agentSpawned(spawningAgent : UUID, parent : Context, agentType : Class, + spawnedAgents : List, initializationParameters : Object[]) { + val identifiers = spawnedAgents.map[it.ID] + this.logger.config [MessageFormat::format(Messages::AgentEventEmitter_0, identifiers)] + // Send the event in the default space. + val defSpace = parent.defaultSpace + assert defSpace !== null, "A context does not contain a default space" + val source = new Address(defSpace.spaceID, spawningAgent ?: parent.ID) + assert source !== null + val ^event = new AgentSpawned(source, agentType.name, identifiers) + // The first parameter is null because the event has an address. + // TODO: Not efficient filtering + defSpace.emit(null, ^event) [!identifiers.contains(it.UUID)] + } + + override agentDestroyed(^agent : Agent, outerContexts : Iterable) { + this.logger.config[MessageFormat::format(Messages::AgentEventEmitter_1, ^agent.ID)] + val size = outerContexts.size + val spaces = newArrayOfSize(size) + val events = newArrayOfSize(size) + var i = 0 + for (contextReference : outerContexts) { + val ^event = new AgentKilled(contextReference.addressInDefaultSpace, ^agent.ID, ^agent.class.name) + spaces.set(i, contextReference.defaultSpace) + events.set(i, ^event) + i++ + } + for (i = 0; i < size; i++) { + // The first parameter is null because the event has an address. + spaces.get(i).emit(null, events.get(i)) [it.UUID != ^agent.ID] + } + } + +} + +/** Emit the context member platform events. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class ContextMemberEventEmitter implements ExternalContextMemberListener { + + val logger : Logger + + /** Constructors. + * + * @param owner the owner of this emitter. + * @param logger the logger to use. + */ + new (logger : Logger) { + this.logger = logger + } + + override memberJoined(joinedContext : AgentContext, joinedContextSpaceId : SpaceID, + joiningAgentID : UUID, joiningAgentType : String) { + val contextID = joinedContext.ID + this.logger.config [ + MessageFormat::format(Messages::ContextMemberEventEmitter_2, joiningAgentID, contextID) + ] + val defSpace = joinedContext.defaultSpace + val source = new Address(joinedContextSpaceId, contextID) + // The first parameter is null because the event has an address. + defSpace.emit(null, new MemberJoined(source, contextID, joiningAgentID, joiningAgentType)) [ + it.UUID != joiningAgentID + ] + } + + override memberLeft(leftContext : AgentContext, leftContextSpaceId : SpaceID, leftMemberID : UUID, + agentType : String) { + val contextID = leftContext.ID + this.logger.config [ + MessageFormat::format( + Messages::ContextMemberEventEmitter_3, + leftMemberID, + contextID + )] + val defSpace = leftContext.defaultSpace + val source = new Address(leftContextSpaceId, leftContext.ID) + // The first parameter is null because the event has an address. + defSpace.emit(null, new MemberLeft(source, leftMemberID, agentType)) [ + it.UUID != leftMemberID + ] + } + +} + +/** Emit the context member platform events. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +@SuppressWarnings("use_reserved_sarl_annotation") +@PrivateAPI(isCallerOnly = true) +class SubHolonContextEventEmitter implements InternalContextMembershipListener { + + val owner : WeakReference + + val lockProvider : Provider + + var behaviorSkill : ClearableReference + + var loggingSkill : ClearableReference + + /** Constructors. + * + * @param owner the owner of this emitter. + * @param lockProvider the provider of synchronization locks. + */ + new (owner : Agent, lockProvider : Provider) { + this.owner = new WeakReference(owner) + this.lockProvider = lockProvider + } + + private def getBehaviors : Behaviors { + var o = this.owner.get + if (o !== null) { + if (this.behaviorSkill?.get === null) { + this.behaviorSkill = SREutils::getInternalSkillReference(o, typeof(Behaviors)) + } + return SREutils::castInternalSkillReference(o, this.behaviorSkill, typeof(Behaviors)) + } + return null + } + + private def getLogging : Logging { + var o = this.owner.get + if (o !== null) { + if (this.loggingSkill?.get === null) { + this.loggingSkill = SREutils::getInternalSkillReference(o, typeof(Logging)) + } + return SREutils::castInternalSkillReference(o, this.loggingSkill, typeof(Logging)) + } + return null + } + + override contextJoined(futureContext : UUID, futureContextDefaultSpaceID : UUID) { + var o = this.owner.get + if (o !== null) { + this.logging.debug[MessageFormat::format(Messages::ContextMemberEventEmitter_0, o.ID, futureContext)] + var evt = new ContextJoined(futureContext, futureContextDefaultSpaceID) + evt.source = getLife(o, this.lockProvider).addressInInnerDefaultSpace + this.behaviors?.wake(evt) + } + } + + override contextLeft(contextID : UUID) { + var o = this.owner.get + if (o !== null) { + this.logging.debug[MessageFormat::format(Messages::ContextMemberEventEmitter_1, o.ID, contextID)] + var evt = new ContextLeft(contextID) + evt.source = getLife(o, this.lockProvider).addressInInnerDefaultSpace + this.behaviors?.wake(evt) + } + } + +} + +/** + * Factory of the listeners on events related to the membership within a context. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.7.0 + */ +class SubHolonContextEventEmitterFactory implements InternalContextMembershipListenerFactory { + + def create(owner : Agent, lockProvider : Provider) : InternalContextMembershipListener { + new SubHolonContextEventEmitter(owner, lockProvider) + } + +} + diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/internal/SmartListenerCollection.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/internal/SmartListenerCollection.sarl new file mode 100644 index 0000000000..74c18c2e2e --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/internal/SmartListenerCollection.sarl @@ -0,0 +1,66 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.internal + +import java.util.EventListener +import java.util.function.Consumer +import javax.inject.Inject +import org.arakhne.afc.util.ListenerCollection + +/** A smart implementation of an event listeners' collection. + * This collection knows how to schedule the calls to the listeners. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +@SuppressWarnings("manual_inline_definition") +class SmartListenerCollection extends ListenerCollection implements ListenerNotifier { + + val notifier : ListenerNotifier + + /** Constructor. + * + * @param notifier is the notifier to the listeners that must be used by this collection. + */ + @Inject + new (notifier : ListenerNotifier) { + this.notifier = notifier + } + + override notifyListeners(listeners : L[], action : Consumer) with L extends EventListener { + this.notifier.notifyListeners(listeners, action) + } + + /** Build a call policy for the given list of listeners and call them. + * + * @param the type of event listener to support. + * @param type the type of the listeners to be called. + * @param action is the action to apply on each listener. + */ + def notifyListeners(type : Class, action : Consumer) with L extends EventListener { + type.getListeners.notifyListeners(action) + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/internal/messages.properties b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/internal/messages.properties new file mode 100644 index 0000000000..7a608dd26c --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/internal/messages.properties @@ -0,0 +1,10 @@ +SpaceEventEmitter_0=Created Space: {0} +SpaceEventEmitter_1=Destroyed Space: {0} +SpaceParticipantEventEmitter_0=Participant Joined: {0} +SpaceParticipantEventEmitter_1=Participant Left: {0} +AgentEventEmitter_0=Agent Spawned: {0} +AgentEventEmitter_1=Agent Killed: {0} +ContextMemberEventEmitter_0=Context Joined: {1}, by: {0} +ContextMemberEventEmitter_1=Context Left: {1}, by: {0} +ContextMemberEventEmitter_2=Member Joined: {0}, into: {1} +ContextMemberEventEmitter_3=Member Left: {0}, from: {1} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/messages.properties b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/messages.properties new file mode 100644 index 0000000000..797f9d625e --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/messages.properties @@ -0,0 +1,4 @@ +Kernel_0=Stopping the kernel services +Kernel_1=All kernel services are stopped +Kernel_2=Starting the kernel services +Kernel_3=experimental SRE, please use with caution diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/AbstractSchemeNameParser.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/AbstractSchemeNameParser.sarl new file mode 100644 index 0000000000..c2ee5edd09 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/AbstractSchemeNameParser.sarl @@ -0,0 +1,133 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.naming + +import java.net.URI +import java.util.StringTokenizer +import org.eclipse.xtend.lib.annotations.Accessors + +/** + * Abstact implementation of a parser of names that is accepting URI-based syntax for a specific scheme + * + * @param the type of the name that is the result of the decoding. + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +abstract class AbstractSchemeNameParser implements ISchemeNameParser { + + @Accessors + val scheme : NameScheme + + /** Constructor. + * + * @param scheme the scheme that is supported by this name parser. + */ + protected new (scheme : NameScheme) { + assert scheme !== null + this.scheme = scheme + } + + /** Refactors the URI. + * + * @param uri the URI to refactor. + * @param minElements is the expected minimum number of path components. + * @param maxElements is the expected maximum number of path components. + * @param lastIsInteger indicates if the last component could be an integer. In this case, + * the expected number of not-integer components becomes [{@code minElements}, {@code maxElements - 1}]. + * @return the new URI. + */ + protected def refactor(uri : URI, minElements : int, maxElements : int, lastIsInteger : boolean = false) : URI { + val newPath = new StringBuilder + if (!uri.host.isNullOrEmpty) { + newPath.append("/").append(uri.host) + } + if (!uri.path.isNullOrEmpty) { + if (!uri.path.startsWith("/")) { + newPath.append("/") + } + newPath.append(uri.path) + } + if (newPath.length === 0) { + if (!uri.schemeSpecificPart.isNullOrEmpty) { + if (!uri.schemeSpecificPart.startsWith("/")) { + newPath.append("/") + } + newPath.append(uri.schemeSpecificPart) + } + } + val path = newPath.toString + if (!path.empty) { + val validatedPath = validatePath(path, minElements, maxElements, lastIsInteger) + if (!validatedPath.isNullOrEmpty) { + return new URI(uri.scheme, null, validatedPath.toString, uri.fragment) + } + } + return null + } + + /** Validate the path. + * + * @param path the path to validate. + * @param minElements is the expected minimum number of path components. + * @param maxElements is the expected maximum number of path components. + * @param lastIsInteger indicates if the last component could be an integer. In this case, + * the expected number of not-integer components becomes [{@code minElements}, {@code maxElements - 1}]. + * @return the validated path. + */ + protected def validatePath(path : String, minElements : int, maxElements : int, lastIsInteger : boolean = false) : String { + if (path.isNullOrEmpty) { + return if ((minElements..maxElements).contains(0)) "" else null + } + var tokenizer = new StringTokenizer(path, "/") + var buffer = new StringBuilder + var lastComponent : String = null + var nb = 0 + while (tokenizer.hasMoreTokens) { + var token = tokenizer.nextToken + if (token.isNullOrEmpty) { + return null + } + buffer.append("/").append(token) + lastComponent = token + nb++ + } + if (lastIsInteger) { + try { + // Fails if the component is not an integer + Integer::parseUnsignedInt(lastComponent, 10) + if ((minElements + 1 .. maxElements).contains(nb)) { + return buffer.toString + } + } catch (ex : Throwable) { + if ((minElements .. maxElements - 1).contains(nb)) { + return buffer.toString + } + } + } else if ((minElements .. maxElements).contains(nb)) { + return buffer.toString + } + return null + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/AgentName.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/AgentName.sarl new file mode 100644 index 0000000000..fc83a2fc50 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/AgentName.sarl @@ -0,0 +1,61 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.naming + +import java.net.URI +import java.util.UUID +import org.eclipse.xtend.lib.annotations.Accessors + +/** + * This class represents a space name. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class AgentName extends SarlName { + + @Accessors + val contextId : UUID + + @Accessors + val spaceId : UUID + + @Accessors + val agentId : UUID + + /** Constructor. + * + * @param uri the uri of the context. + * @param contextId the identifier of the context. + * @param spaceId the identifier of the space. + * @param agentId the identifier of the agent. + */ + protected new (uri : URI, contextId : UUID, spaceId : UUID, agentId : UUID) { + super(uri) + this.contextId = contextId + this.spaceId = spaceId + this.agentId = agentId + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/AgentSchemeNameParser.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/AgentSchemeNameParser.sarl new file mode 100644 index 0000000000..c2431a351e --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/AgentSchemeNameParser.sarl @@ -0,0 +1,76 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.naming + +import java.net.URI +import java.util.StringTokenizer +import java.util.UUID + +/** + * Parser of agent names that is accepting URI-based syntax. + * + *

The different types of names are:

+ * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class AgentSchemeNameParser extends AbstractSchemeNameParser { + + /** Constructor. + * + * @param scheme the name scheme that is supported by this parser. By default it is {@link NameScheme.AGENT}. + */ + new (scheme : NameScheme = NameScheme::AGENT) { + super(scheme) + } + + @Pure + override refactor(name : URI) : URI { + return name.refactor(1, 3) + } + + @Pure + override decode(name : URI) : AgentName { + val tokenizer = new StringTokenizer(name.path, "/") + if (tokenizer.hasMoreTokens) { + val token0 = tokenizer.nextToken + if (tokenizer.hasMoreTokens) { + val token1 = tokenizer.nextToken + if (tokenizer.hasMoreTokens) { + val token2 = tokenizer.nextToken + return new AgentName(name, UUID::fromString(token0), UUID::fromString(token1), + UUID::fromString(token2)) + } + return new AgentName(name, UUID::fromString(token0), null, UUID::fromString(token1)) + } + return new AgentName(name, null, null, UUID::fromString(token0)) + } + return null + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/BehaviorName.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/BehaviorName.sarl new file mode 100644 index 0000000000..a31af6ee41 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/BehaviorName.sarl @@ -0,0 +1,73 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.naming + +import io.sarl.lang.core.Behavior +import java.net.URI +import java.util.UUID +import org.eclipse.xtend.lib.annotations.Accessors + +/** + * This class represents a behavior name. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class BehaviorName extends SarlName { + + @Accessors + val contextId : UUID + + @Accessors + val spaceId : UUID + + @Accessors + val agentId : UUID + + @Accessors + val behaviorType : Class + + @Accessors + val behaviorIndex : int + + /** Constructor. + * + * @param uri the uri of the context. + * @param contextId the identifier of the context. + * @param spaceId the identifier of the space. + * @param agentId the identifier of the agent. + * @param behaviorType the type of the behavior. + * @param behaviorIndex the index of the behavior in the list of behaviors. + */ + protected new (uri : URI, contextId : UUID, spaceId : UUID, agentId : UUID, + behaviorType : Class, behaviorIndex : int) { + super(uri) + this.contextId = contextId + this.spaceId = spaceId + this.agentId = agentId + this.behaviorType = behaviorType + this.behaviorIndex = behaviorIndex + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/BehaviorSchemeNameParser.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/BehaviorSchemeNameParser.sarl new file mode 100644 index 0000000000..dc00d6ef2d --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/BehaviorSchemeNameParser.sarl @@ -0,0 +1,107 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.naming + +import io.sarl.lang.core.Behavior +import java.net.URI +import java.util.StringTokenizer +import java.util.UUID + +/** + * Parser of behavior names that is accepting URI-based syntax. + * + *

The different types of names are:

    + *
  • {@code "behavior:[/]{0-2}agentId/behaviorName[/behaviorIndex][#fragmentName]"}
  • + *
  • {@code "behavior:[/]{0-2}contextId/agentId/behaviorName[/behaviorIndex][#fragmentName]"}
  • + *
  • {@code "behavior:[/]{0-2}contextId/spaceId/agentId/behaviorName[/behaviorIndex][#fragmentName]"}
  • + *
+ * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class BehaviorSchemeNameParser extends AbstractSchemeNameParser { + + /** Constructor. + * + * @param scheme the name scheme that is supported by this parser. By default it is {@link NameScheme.BEHAVIOR}. + */ + new (scheme : NameScheme = NameScheme::BEHAVIOR) { + super(scheme) + } + + @Pure + override refactor(name : URI) : URI { + return name.refactor(2, 5, true) + } + + @Pure + override decode(name : URI) : BehaviorName { + val tokenizer = new StringTokenizer(name.path, "/") + if (tokenizer.hasMoreTokens) { + val token0 = tokenizer.nextToken + if (tokenizer.hasMoreTokens) { + val token1 = tokenizer.nextToken + if (tokenizer.hasMoreTokens) { + val token2 = tokenizer.nextToken + if (tokenizer.hasMoreTokens) { + val token3 = tokenizer.nextToken + if (tokenizer.hasMoreTokens) { + // 5 components + val token4 = tokenizer.nextToken + return new BehaviorName(name, UUID::fromString(token0), + UUID::fromString(token1), UUID::fromString(token2), + Class::forName(token3) as Class, + Integer.parseUnsignedInt(token4, 10)) + } + // 4 components + try { + var beh = Class::forName(token2) as Class + return new BehaviorName(name, UUID::fromString(token0), null, + UUID::fromString(token1), beh, Integer.parseUnsignedInt(token3, 10)) + } catch (ex : Throwable) { + return new BehaviorName(name, UUID::fromString(token0), UUID::fromString(token1), + UUID::fromString(token2), Class::forName(token3) as Class, + -1) + } + } + // 3 components + try { + var beh = Class::forName(token1) as Class + return new BehaviorName(name, null, null, UUID::fromString(token0), + beh, Integer.parseUnsignedInt(token2, 10)) + } catch (ex : Throwable) { + return new BehaviorName(name, UUID::fromString(token0), null, + UUID::fromString(token1), Class::forName(token2) as Class, + -1) + } + } + // 2 components + return new BehaviorName(name, null, null, UUID::fromString(token0), + Class::forName(token1) as Class, -1) + } + } + return null + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/ContextName.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/ContextName.sarl new file mode 100644 index 0000000000..b0f5cef97b --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/ContextName.sarl @@ -0,0 +1,51 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.naming + +import java.net.URI +import java.util.UUID +import org.eclipse.xtend.lib.annotations.Accessors + +/** + * This class represents a context name. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class ContextName extends SarlName { + + @Accessors + val contextId : UUID + + /** Constructor. + * + * @param uri the uri of the context. + * @param contextId the identifier of the context. + */ + protected new (uri : URI, contextId : UUID) { + super(uri) + this.contextId = contextId + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/ContextSchemeNameParser.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/ContextSchemeNameParser.sarl new file mode 100644 index 0000000000..f2c64a4f4d --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/ContextSchemeNameParser.sarl @@ -0,0 +1,65 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.naming + +import java.net.URI +import java.util.StringTokenizer +import java.util.UUID + +/** + * Parser of context names that is accepting URI-based syntax. + * + *

The different types of names are:

    + *
  • {@code "context:[/]{0-2}contextId[#fragmentName]"}
  • + *
+ * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class ContextSchemeNameParser extends AbstractSchemeNameParser { + + /** Constructor. + * + * @param scheme the name scheme that is supported by this parser. By default it is {@link NameScheme.CONTEXT}. + */ + new (scheme : NameScheme = NameScheme::CONTEXT) { + super(scheme) + } + + @Pure + override refactor(name : URI) : URI { + return name.refactor(1, 1) + } + + @Pure + override decode(name : URI) : ContextName { + val tokenizer = new StringTokenizer(name.path, "/") + if (tokenizer.hasMoreTokens) { + val token = tokenizer.nextToken + return new ContextName(name, UUID::fromString(token)) + } + return null + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/INameParser.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/INameParser.sarl new file mode 100644 index 0000000000..81f917355b --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/INameParser.sarl @@ -0,0 +1,101 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.naming + +import java.net.URI + +/** + * Parser of names + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +interface INameParser { + + /** Parse the scheme. + */ + @Pure + static def getSchemeObject(scheme : String) : NameScheme { + if (scheme !== null) { + for (s : NameScheme::values) { + if (scheme.equalsIgnoreCase(s.name)) { + return s + } + } + } + return null + } + + /** + * Parse the string representation of the name, and create the URI representation. + * + * @param name the string representation of the name. + * @return the URI representation of the given name, or {@code null} if the given string cannot be parsed. + */ + @Pure + def decode(name : String) : URI { + if (!name.isNullOrEmpty) { + try { + var uri = URI::create(name) + return normalize(uri) + } catch (ex : Throwable) { + // + } + } + return null + } + + /** + * Parse the string representation of the name, and create the URI representation. + * + * @param name the string representation of the name. + * @return the URI representation of the given name, or {@code null} if the given string cannot be parsed. + */ + @Pure + def normalize(name : URI) : URI + + /** + * Parse the URI of the name, and create the name object. + * The argument must be a normalized URI that is computed by {@link #normalize(URI)}. + * + * @param name the normalized URI representation of the name. See {@link #normalize(URI)}. + * @return the name, or {@code null} if the given URI cannot be parsed. + */ + @Pure + def decode(name : URI) : SarlName + + /** Register a name parser for a specific scheme. + * + * @param parser the name parser, never {@code null}. + */ + def addSchemeNameParser(parser : ISchemeNameParser) + + /** Unregister a name parser for a specific scheme. + * + * @param scheme the associated scheme, never {@code null}. + * @return the name parser that was associated to the name protocol. + */ + def removeSchemeNameParser(scheme: NameScheme) : ISchemeNameParser + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/ISchemeNameParser.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/ISchemeNameParser.sarl new file mode 100644 index 0000000000..e203c33531 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/ISchemeNameParser.sarl @@ -0,0 +1,50 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.naming + +import java.net.URI + +/** + * A parser of names that is accepting URI-based syntax for a specific scheme. + * + * @param the type of the name that is the result of the decoding. + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +interface ISchemeNameParser { + + /** Replies the name scheme that is supported by this parser. */ + @Pure + def getScheme : NameScheme + + /** Refactor the given URI in order to fit the name specification. */ + @Pure + def refactor(name : URI) : URI + + /** Decode the name. + */ + @Pure + def decode(name : URI) : N + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/NameParser.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/NameParser.sarl new file mode 100644 index 0000000000..bf5d0ba8bd --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/NameParser.sarl @@ -0,0 +1,106 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.naming + +import io.sarl.sre.boot.internal.naming.SchemeNameParsers +import java.net.URI +import java.util.Set +import javax.inject.Inject + +/** + * Parser of names that is accepting URI-based syntax. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class NameParser implements INameParser { + + val schemeNameParser = >newTreeMap(null) + + /** Change the set of scheme name parsers that is used by this base name parser. + * + *

This function is annotated in order to be used by the Guice injector. + * + * @param parsers the set of scheme name parsers. + */ + @SuppressWarnings("raw_type") + @Inject + def setSchemeNameParsers(@SchemeNameParsers parsers : Set) { + if (parsers !== null) { + this.schemeNameParser.clear + for (parser : parsers) { + parser.addSchemeNameParser + } + } + } + + override addSchemeNameParser(parser : ISchemeNameParser) { + assert parser !== null + this.schemeNameParser.put(parser.scheme, parser) + } + + override removeSchemeNameParser(scheme : NameScheme) : ISchemeNameParser { + assert scheme !== null + this.schemeNameParser.remove(scheme) + } + + @Pure + def normalize(name : URI) : URI { + try { + val scheme = name.scheme + val schemeObj = scheme.schemeObject + if (schemeObj !== null + && name.query.isNullOrEmpty + && name.userInfo.isNullOrEmpty + && name.port === -1) { + var parser = this.schemeNameParser.get(schemeObj) + if (parser !== null) { + return parser.refactor(name) + } + } + } catch (ex : Throwable) { + // + } + return null + } + + @Pure + override decode(name : URI) : SarlName { + try { + if (name !== null && name.path !== null && name.path.startsWith("/")) { + var scheme = name.scheme.getSchemeObject + if (scheme !== null) { + var parser = this.schemeNameParser.get(scheme) + if (parser !== null) { + return parser.decode(name) + } + } + } + } catch (ex : Throwable) { + // + } + return null + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/NameScheme.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/NameScheme.sarl new file mode 100644 index 0000000000..66374ac8b1 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/NameScheme.sarl @@ -0,0 +1,34 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.naming + +/** + * Name of a scheme that is supported for names' specification. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +enum NameScheme { + CONTEXT, SPACE, AGENT, SKILL, BEHAVIOR, SERVICE +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/SarlName.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/SarlName.sarl new file mode 100644 index 0000000000..8aaddb292e --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/SarlName.sarl @@ -0,0 +1,99 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.naming + +import java.io.Serializable +import java.lang.ref.WeakReference +import java.net.URI + +/** + * This class represents a component name. Each component in a name is an atomic name. + * + *

The components of a name are numbered. The indexes of a + * name with N components range from 0 up to, but not including, N. + * An empty compound name has no components. + * + *

Multithreaded Access

+ * A {@code SName} instance is not synchronized against concurrent + * multithreaded access. Multiple threads trying to access and modify a + * {@code SName} should lock the object. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +abstract class SarlName implements Cloneable, Serializable, Comparable { + + transient var associatedObject : WeakReference + + val uri : URI + + var scheme : NameScheme + + protected new (uri : URI) { + assert uri !== null + this.uri = uri + } + + /** Replies the scheme of this name. */ + def getScheme : NameScheme { + if (this.scheme === null) { + this.scheme = INameParser::getSchemeObject(toURI.scheme) + } + return this.scheme + } + + @Pure + override compareTo(name : SarlName) : int { + if (name === null) 1 else this.uri.compareTo(name.uri) + } + + /** Replies the URI associated to this name. */ + def toURI : URI { + this.uri + } + + /** Replies the name of the fragment. */ + def getFragment : String { + this.uri.fragment + } + + /** Replies if this name has a fragment associated to it. + */ + def hasFragment : boolean { + !this.uri.fragment.isNullOrEmpty + } + + /** Replies the associated object. + */ + final def getAssociatedObject : Object { + this.associatedObject?.get + } + + /** Change the associated object. + */ + final def setAssociatedObject(obj : Object) { + this.associatedObject = if (obj === null) null else new WeakReference(obj) + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/ServiceName.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/ServiceName.sarl new file mode 100644 index 0000000000..764f82bde7 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/ServiceName.sarl @@ -0,0 +1,51 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.naming + +import com.google.common.util.concurrent.Service +import java.net.URI +import org.eclipse.xtend.lib.annotations.Accessors + +/** + * This class represents a service name. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class ServiceName extends SarlName { + + @Accessors + val serviceType : Class + + /** Constructor. + * + * @param uri the uri of the context. + * @param service the type of service. + */ + protected new (uri : URI, service : Class) { + super(uri) + this.serviceType = service + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/ServiceSchemeNameParser.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/ServiceSchemeNameParser.sarl new file mode 100644 index 0000000000..a7ba936c89 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/ServiceSchemeNameParser.sarl @@ -0,0 +1,65 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.naming + +import com.google.common.util.concurrent.Service +import java.net.URI +import java.util.StringTokenizer + +/** + * Parser of service names that is accepting URI-based syntax. + * + *

The different types of names are:

    + *
  • {@code "service:[/]{0-2}serviceName][#fragmentName]"}
  • + *
+ * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class ServiceSchemeNameParser extends AbstractSchemeNameParser { + + /** Constructor. + * + * @param scheme the name scheme that is supported by this parser. By default it is {@link NameScheme.SERVICE}. + */ + new (scheme : NameScheme = NameScheme::SERVICE) { + super(scheme) + } + + @Pure + override refactor(name : URI) : URI { + return name.refactor(1, 1) + } + + @Pure + override decode(name : URI) : ServiceName { + val tokenizer = new StringTokenizer(name.path, "/") + if (tokenizer.hasMoreTokens) { + val token = tokenizer.nextToken + return new ServiceName(name, Class::forName(token) as Class) + } + return null + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/SkillName.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/SkillName.sarl new file mode 100644 index 0000000000..c7c5d287b3 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/SkillName.sarl @@ -0,0 +1,67 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.naming + +import io.sarl.lang.core.Capacity +import java.net.URI +import java.util.UUID +import org.eclipse.xtend.lib.annotations.Accessors + +/** + * This class represents a skill name. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class SkillName extends SarlName { + + @Accessors + val contextId : UUID + + @Accessors + val spaceId : UUID + + @Accessors + val agentId : UUID + + @Accessors + val ^capacity : Class + + /** Constructor. + * + * @param uri the uri of the context. + * @param contextId the identifier of the context. + * @param spaceId the identifier of the space. + * @param agentId the identifier of the agent. + * @param capacity the name of the capacity implemented by the skill. + */ + protected new (uri : URI, contextId : UUID, spaceId : UUID, agentId : UUID, ^capacity : Class) { + super(uri) + this.contextId = contextId + this.spaceId = spaceId + this.agentId = agentId + this.^capacity = ^capacity + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/SkillSchemeNameParser.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/SkillSchemeNameParser.sarl new file mode 100644 index 0000000000..5b6d6bda2e --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/SkillSchemeNameParser.sarl @@ -0,0 +1,82 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.naming + +import io.sarl.lang.core.Capacity +import java.net.URI +import java.util.StringTokenizer +import java.util.UUID + +/** + * Parser of skill names that is accepting URI-based syntax. + * + *

The different types of names are:

    + *
  • {@code "skill:[/]{0-2}agentId/capacityName[#fragmentName]"}
  • + *
  • {@code "skill:[/]{0-2}contextId/agentId/capacityName[#fragmentName]"}
  • + *
  • {@code "skill:[/]{0-2}contextId/spaceId/agentId/capacityName[#fragmentName]"}
  • + *
+ * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class SkillSchemeNameParser extends AbstractSchemeNameParser { + + /** Constructor. + * + * @param scheme the name scheme that is supported by this parser. By default it is {@link NameScheme.SKILL}. + */ + new (scheme : NameScheme = NameScheme::SKILL) { + super(scheme) + } + + @Pure + override refactor(name : URI) : URI { + return name.refactor(2, 4) + } + + @Pure + override decode(name : URI) : SkillName { + val tokenizer = new StringTokenizer(name.path, "/") + if (tokenizer.hasMoreTokens) { + val token0 = tokenizer.nextToken + if (tokenizer.hasMoreTokens) { + val token1 = tokenizer.nextToken + if (tokenizer.hasMoreTokens) { + val token2 = tokenizer.nextToken + if (tokenizer.hasMoreTokens) { + val token3 = tokenizer.nextToken + return new SkillName(name, UUID::fromString(token0), UUID::fromString(token1), + UUID::fromString(token2), Class::forName(token3) as Class) + } + return new SkillName(name, UUID::fromString(token0), null, UUID::fromString(token1), + Class::forName(token2) as Class) + } + return new SkillName(name, null, null, UUID::fromString(token0), + Class::forName(token1) as Class) + } + } + return null + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/SpaceName.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/SpaceName.sarl new file mode 100644 index 0000000000..aa7b6fc2cd --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/SpaceName.sarl @@ -0,0 +1,56 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.naming + +import java.net.URI +import java.util.UUID +import org.eclipse.xtend.lib.annotations.Accessors + +/** + * This class represents a space name. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class SpaceName extends SarlName { + + @Accessors + val contextId : UUID + + @Accessors + val spaceId : UUID + + /** Constructor. + * + * @param uri the uri of the context. + * @param contextId the identifier of the context. + * @param spaceId the identifier of the context. + */ + protected new (uri : URI, contextId : UUID, spaceId : UUID) { + super(uri) + this.contextId = contextId + this.spaceId = spaceId + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/SpaceSchemeNameParser.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/SpaceSchemeNameParser.sarl new file mode 100644 index 0000000000..291686e142 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/naming/SpaceSchemeNameParser.sarl @@ -0,0 +1,68 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.naming + +import java.net.URI +import java.util.StringTokenizer +import java.util.UUID + +/** + * Parser of space names that is accepting URI-based syntax. + * + *

The different types of names are:

    + *
  • {@code "space:[/]{0-2}contextId/spaceId[#fragmentName]"}
  • + *
+ * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class SpaceSchemeNameParser extends AbstractSchemeNameParser { + + /** Constructor. + * + * @param scheme the name scheme that is supported by this parser. By default it is {@link NameScheme.SPACE}. + */ + new (scheme : NameScheme = NameScheme::SPACE) { + super(scheme) + } + + @Pure + override refactor(name : URI) : URI { + return name.refactor(2, 2) + } + + @Pure + override decode(name : URI) : SpaceName { + val tokenizer = new StringTokenizer(name.path, "/") + if (tokenizer.hasMoreTokens) { + val token0 = tokenizer.nextToken + if (tokenizer.hasMoreTokens) { + val token1 = tokenizer.nextToken + return new SpaceName(name, UUID::fromString(token0), UUID::fromString(token1)) + } + } + return null + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/AbstractServiceManager.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/AbstractServiceManager.sarl new file mode 100644 index 0000000000..57720728e4 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/AbstractServiceManager.sarl @@ -0,0 +1,89 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services + +import java.util.logging.Logger + +/** + * Abstract implementation of a service manager for the SRE platform. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +abstract class AbstractServiceManager implements IServiceManager { + + var logger : Logger + + /** Replies the logger. + * + * @return the logger. + */ + protected def getLogger : Logger { + return this.logger + } + + /** Change the logger. + * + * @param logger the new logger. + */ + protected def setLogger(logger : Logger) { + this.logger = logger + } + + def startServices(logger : Logger) { + setLogger(logger) + internalStartAllServices + // Await for stop + logger.fine(Messages::AbstractServiceManager_1) + awaitHealthy + } + + def stopServices(logger : Logger) { + preStop(logger) + logger.fine(Messages::AbstractServiceManager_3) + internalStopAllServices + // Await for stop + logger.fine(Messages::AbstractServiceManager_0) + awaitStopped + } + + /** Pre-stop all the services that are implemented {@link + */ + protected def preStop(logger : Logger) : void { + logger.fine(Messages::AbstractServiceManager_4) + for (service : servicesByState.values) { + if (service.isRunning && service instanceof PreReleasableService) { + (service as PreReleasableService).onPreStop + } + } + } + + /** Start all the services whatever the dependencies between the services. + */ + protected abstract def internalStartAllServices + + /** Stop all the services whatever the dependencies between the services. + */ + protected abstract def internalStopAllServices + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/AbstractSreService.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/AbstractSreService.sarl new file mode 100644 index 0000000000..25b7a272c8 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/AbstractSreService.sarl @@ -0,0 +1,73 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services + +import com.google.common.util.concurrent.AbstractService +import io.sarl.lang.annotation.NoEqualityTestFunctionsGeneration + +/** + * Abstract implementation of a typical SRE service. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.7.0 + */ +@SuppressWarnings("use_reserved_sarl_annotation") +@NoEqualityTestFunctionsGeneration +abstract class AbstractSreService extends AbstractService { + + protected final override doStart { + try { + onStart + notifyStarted + } catch (exception : Throwable) { + notifyFailed(exception) + } + } + + /** Do something when starting the service. + * + *

This function is called from {@link #doStart()} + */ + protected def onStart { + // + } + + protected final override doStop { + try { + onStop + notifyStopped + } catch (exception : Throwable) { + notifyFailed(exception) + } + } + + /** Do something when stopping the service. + * + *

This function is called from {@link #doStop()} + */ + protected def onStop { + // + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/GoogleServiceManager.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/GoogleServiceManager.sarl new file mode 100644 index 0000000000..49e1ed09a6 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/GoogleServiceManager.sarl @@ -0,0 +1,107 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services + +import com.google.common.collect.Multimap +import com.google.common.util.concurrent.Service +import com.google.common.util.concurrent.ServiceManager +import com.google.common.util.concurrent.ServiceManager.Listener +import io.sarl.sre.boot.configs.SreConfig +import io.sarl.sre.boot.configs.subconfigs.ServicesConfig +import java.text.MessageFormat +import java.util.concurrent.TimeUnit +import java.util.concurrent.TimeoutException + +/** + * Implementation of a service manager based on the Google service manager. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class GoogleServiceManager extends AbstractServiceManager { + + val googleManager : ServiceManager + + val configuration : ServicesConfig + + /** + * @param sm the Google service to use. + * @param config the configuration. + */ + new (sm : ServiceManager, config : SreConfig) { + this.configuration = config.services + this.googleManager = sm + this.googleManager.addListener(new Listener() { + def failure(service : Service) { + getLogger.severe(MessageFormat::format(Messages::GoogleServiceManager_0, service.class.name)) + } + }) + } + + /** + * @param services the services to manager. + * @param config the configuration. + */ + new (services : Iterable, config : SreConfig) { + this(new ServiceManager(services), config) + } + + def getServicesByState : Multimap { + this.googleManager.servicesByState + } + + def getService(type : Class) : T with T extends Service { + this.googleManager.servicesByState.values.findFirst[type.isInstance(it)] as T + } + + def awaitHealthy { + var timeout = this.configuration.startTimeout + if (timeout > 0) { + this.googleManager.awaitHealthy(timeout, TimeUnit::MILLISECONDS) + } else { + this.googleManager.awaitHealthy + } + } + + protected override internalStartAllServices { + this.googleManager.startAsync + } + + protected override internalStopAllServices { + this.googleManager.stopAsync + } + + def awaitStopped { + try { + var timeout = this.configuration.stopTimeout + if (timeout > 0) { + this.googleManager.awaitStopped(timeout, TimeUnit::MILLISECONDS) + } else { + this.googleManager.awaitStopped + } + } catch (timeout : TimeoutException) { + // stopping timed out + } + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/IServiceManager.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/IServiceManager.sarl new file mode 100644 index 0000000000..a56dbc8d47 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/IServiceManager.sarl @@ -0,0 +1,80 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services + +import com.google.common.collect.Multimap +import com.google.common.util.concurrent.Service +import java.util.logging.Logger + +/** + * Manager of services for the SRE platform. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +interface IServiceManager { + + /** + * Replies the services by state. + * + * @return the services. + */ + def getServicesByState : Multimap + + /** + * Replies the service of the given type. + * + * @param type the type of the service to search for. + * @return the service + */ + def getService(type : Class) : T with T extends Service + + /** + * Wait for all the services are started. + */ + def awaitHealthy + + /** + * Wait for all the services are stopped. + */ + def awaitStopped + + /** + * Start the services associated to the service manager. + * + *

This starting function supports the {@link DependentService prioritized services}. + * + * @param logger the logger to use for any information message. + */ + def startServices(logger : Logger) + + /** + * Stop the services associated to the service manager. + * + *

This stopping function supports the {@link DependentService prioritized services}. + * + * @param logger the logger to use for any information message. + */ + def stopServices(logger : Logger) + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/Messages.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/Messages.sarl new file mode 100644 index 0000000000..be680e3c17 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/Messages.sarl @@ -0,0 +1,61 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services + +import org.eclipse.osgi.util.NLS + +/** Messages. + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +final class Messages extends NLS { + + static val BUNDLE_NAME = typeof(Messages).getPackage.name + ".messages" + + static new { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, typeof(Messages)) + } + + public static var StartingPhaseAccessors_0 : String + public static var StartingPhaseAccessors_1 : String + public static var StartingPhaseAccessors_2 : String + + public static var StoppingPhaseAccessors_0 : String + public static var StoppingPhaseAccessors_1 : String + public static var StoppingPhaseAccessors_2 : String + + public static var AbstractServiceManager_0 : String + public static var AbstractServiceManager_1 : String + public static var AbstractServiceManager_2 : String + public static var AbstractServiceManager_3 : String + public static var AbstractServiceManager_4 : String + + public static var GoogleServiceManager_0 : String + + + private new { + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/PreReleasableService.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/PreReleasableService.sarl new file mode 100644 index 0000000000..6732c559d7 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/PreReleasableService.sarl @@ -0,0 +1,43 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services + +import com.google.common.util.concurrent.Service + +/** + * This service could be pre-released before it is stop. + * It means that the service could release any resource before the + * full stopping process is run. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.7.0 + */ +interface PreReleasableService extends Service { + + /** + * Invoked when the service should release resources prior to the shutdown process. + */ + def onPreStop + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/AbstractContextService.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/AbstractContextService.sarl new file mode 100644 index 0000000000..3b97575954 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/AbstractContextService.sarl @@ -0,0 +1,165 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.context + +import io.sarl.sre.services.AbstractSreService +import java.util.Map +import java.util.UUID +import java.util.concurrent.locks.ReadWriteLock +import javax.inject.Singleton +import org.eclipse.xtend.lib.annotations.Accessors +import io.sarl.lang.core.Agent + +/** + * A service managing the root context and enabling the creation of contexts. + * + *

This service is thread-safe. + * + * @author $Author: ngaud$ + * @author $Author: srodriguez$ + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +@Singleton +abstract class AbstractContextService extends AbstractSreService implements ContextService { + + val lock : ReadWriteLock + + @Accessors(PUBLIC_GETTER) + val rootContext : Context + + /** Constructor. + * + * @param rootContext the root context. + * @param lock is the lock that must be used by this service. + */ + protected new (rootContext : Context, lock : ReadWriteLock) { + this.rootContext = rootContext + this.lock = lock + } + + def onStart { + this.rootContext.initialize + } + + def onStop { + this.rootContext.destroy + } + + /** Replies the lock for synchronizing this service + */ + def getLock : ReadWriteLock { + this.lock + } + + /** Replies the internal data structure for storing the contexts. + * + *

This function is not thread-safe. + */ + protected abstract def getContextInternalStructure : Map + + final def createContextWithoutRegistration(contextID : UUID, defaultSpaceID : UUID, owner : Agent) : Context { + assert contextID !== null, "The contextID cannot be null" + assert defaultSpaceID !== null, "The defaultSpaceUUID cannot be null" + return contextID.newContextInstance(defaultSpaceID, owner) + } + + final def createContext(contextID : UUID, defaultSpaceID : UUID, owner : Agent) : Context { + assert contextID !== null, "The contextID cannot be null" + assert this.rootContext !== null, "No root context yet" + if (contextID == this.rootContext.ID) { + return this.rootContext + } + val repository = getContextInternalStructure + var existingContext : Context + var lck = getLock + lck.readLock.lock + try { + existingContext = repository.get(contextID) + } finally { + lck.readLock.unlock + } + if (existingContext === null) { + assert defaultSpaceID !== null, "The defaultSpaceUUID cannot be null" + lck.writeLock.lock + try { + existingContext = repository.get(contextID) + if (existingContext === null) { + existingContext = contextID.newContextInstance(defaultSpaceID, owner) + repository.putIfAbsent(contextID, existingContext) + } + } finally { + lck.writeLock.unlock + } + } + return existingContext + } + + def getContext(contextID : UUID) : Context { + assert contextID !== null, "The contextID cannot be null" + assert rootContext !== null, "The root context cannot be null" + if (contextID == this.rootContext.ID) { + return this.rootContext + } + val repository = contextInternalStructure + var existingContext : Context + var lck = getLock + lck.readLock.lock + try { + existingContext = repository.get(contextID) + } finally { + lck.readLock.unlock + } + return existingContext + } + + def removeContext(contextID : UUID) : Context { + assert contextID !== null, "The contextID cannot be null" + if (contextID == this.rootContext.ID) { + return null + } + val repository = contextInternalStructure + var existingContext : Context + var lck = getLock + lck.writeLock.lock + try { + existingContext = repository.remove(contextID) + } finally { + lck.writeLock.unlock + } + if (existingContext !== null) { + existingContext.destroy + } + return existingContext + } + + /** Create a context instance. + * + * @param contextID the identifier of the context. + * @param defaultSpaceId the identifier of the default space in the context. + * @param owner is the owner of the context. If {@code null}, the context is the root context + */ + protected abstract def newContextInstance(contextID : UUID, defaultSpaceID : UUID, + owner : Agent) : Context + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/AbstractInjectionBasedContextService.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/AbstractInjectionBasedContextService.sarl new file mode 100644 index 0000000000..8b1eb3d58c --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/AbstractInjectionBasedContextService.sarl @@ -0,0 +1,65 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.context + +import com.google.inject.Injector +import java.util.UUID +import java.util.concurrent.locks.ReadWriteLock +import io.sarl.lang.core.Agent + +/** + * A service managing the root context and enabling the creation of contexts by injection. + * + *

This service is thread-safe. + * + * @author $Author: ngaud$ + * @author $Author: srodriguez$ + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +abstract class AbstractInjectionBasedContextService extends AbstractContextService { + + val injector : Injector + + val contextFactory : ContextFactory + + /** Constructor. + * + * @param rootContext the root context. + * @param injector is the injector. + * @param factory is the factory of contexts. + * @param lock is the lock that must be used by this service. + */ + protected new(rootContext : Context, injector : Injector, factory : ContextFactory, lock : ReadWriteLock) { + super(rootContext, lock) + this.injector = injector + this.contextFactory = factory + } + + protected def newContextInstance(contextID : UUID, defaultSpaceID : UUID, owner : Agent) : Context { + var context = this.contextFactory.newInstance(contextID, defaultSpaceID, owner) + this.injector.injectMembers(context) + return context + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/Context.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/Context.sarl new file mode 100644 index 0000000000..3d84fe94e6 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/Context.sarl @@ -0,0 +1,370 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.context + +import com.google.inject.ImplementedBy +import io.sarl.core.OpenEventSpace +import io.sarl.core.OpenEventSpaceSpecification +import io.sarl.lang.core.Agent +import io.sarl.lang.core.AgentContext +import io.sarl.lang.core.AgentTrait +import io.sarl.lang.core.Space +import io.sarl.lang.core.SpaceID +import io.sarl.lang.core.SpaceSpecification +import io.sarl.lang.util.SynchronizedIterable +import io.sarl.sre.KernelScope +import io.sarl.sre.services.logging.LoggingService +import java.util.UUID +import java.util.concurrent.locks.ReadWriteLock +import javax.inject.Inject +import javax.inject.Provider +import org.eclipse.xtext.xbase.lib.util.ToStringBuilder + +/** + * Implementation of an agent context in the SRE platform. + * + *

This class is thread-safe. + * + * @author $Author: srodriguez$ + * @author $Author: ngaud$ + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class Context extends AgentTrait implements AgentContext { + + val id : UUID + + val defaultSpaceID : UUID + + var repositoryInstance : SpaceRepository + + var defaultSpaceInstance : OpenEventSpace + + val repositoryProvider : Provider + + val logger : LoggingService + + /** Emitter of the platform events: SpaceCreated, SpaceDestroy */ + var platformEventEmitter : SpaceRepositoryListener + + val spaceEventEmitterFactory : SpaceRepositoryListenerFactory + + val lock : ReadWriteLock + + /** + * Constructs a {@code Context}. + * + * @param id identifier of the context. + * @param defaultSpaceID identifier of the default space in the context. + * @param owner is the owner of the context. If {@code null}, it is the root context. + * @param lock the synchronization lock to use. + * @param spaceRepositoryProvider the provider of space repository + * @param logger the logging service. + * @param listenerFactory the factory of space listeners. + */ + new (id : UUID, defaultSpaceID : UUID, owner : Agent, lock : ReadWriteLock, + spaceRepositoryProvider : Provider, logger : LoggingService, + listenerFactory : SpaceRepositoryListenerFactory) { + super(owner) + this.id = id + this.defaultSpaceID = defaultSpaceID + this.lock = lock + this.repositoryProvider = spaceRepositoryProvider + this.logger = logger + this.spaceEventEmitterFactory = listenerFactory + } + + /** Replies if the context is a root context. + * + *

A root context is associated to the platform kernel agent, which is not created into memory. + * For example, it means that there is no parent registered into the default space. + * + * @return {@code true} if the context is a root context. + */ + def isRootContext : boolean { + this.owner === null + } + + def getID : UUID { + this.id + } + + /** Replies the lock used for synchronizing this context. */ + def getLock : ReadWriteLock { + this.lock + } + + @Pure + override toString(builder : ToStringBuilder) { + builder.add("id", getID) + builder.add("owner", getOwner) + } + + private def getPlatformEventEmitter : SpaceRepositoryListener { + var instance = this.platformEventEmitter + if (instance === null) { + instance = this.spaceEventEmitterFactory.create(ID, defaultSpace, this.logger.kernelLogger) + this.platformEventEmitter = instance + } + return instance + } + + private def ensureRepository : SpaceRepository { + var instance : SpaceRepository + var addListener = false + var lck = getLock + lck.readLock.lock + try { + instance = this.repositoryInstance + } finally { + lck.readLock.unlock + } + if (instance === null) { + lck.writeLock.lock + try { + instance = this.repositoryInstance + if (instance === null) { + instance = this.repositoryProvider.get + this.repositoryInstance = instance + addListener = true + } + } finally { + lck.writeLock.unlock + } + } + + var ds : OpenEventSpace + lck.readLock.lock + try { + ds = this.defaultSpaceInstance + } finally { + lck.readLock.unlock + } + if (ds === null) { + lck.writeLock.lock + try { + ds = this.defaultSpaceInstance + if (ds === null) { + var spaceID = new SpaceID(ID, this.defaultSpaceID, typeof(OpenEventSpaceSpecification)) + ds = instance.createDefaultSpace(spaceID) + this.defaultSpaceInstance = ds + } + } finally { + lck.writeLock.unlock + } + } + if (addListener) { + instance.addSpaceRepositoryListener(getPlatformEventEmitter) + } + return instance + } + /** + * Initialize the context when it is published to the agents. + */ + def initialize { + } + + /** + * Destroy any associated resources. + */ + def destroy { + this.platformEventEmitter = null + var lck = getLock + lck.writeLock.lock + try { + this.defaultSpaceInstance = null + var instance = this.repositoryInstance + if (instance !== null) { + instance.destroy + instance.removeSpaceRepositoryListener(getPlatformEventEmitter) + } + this.repositoryInstance = null + } finally { + lck.writeLock.unlock + } + } + + def getDefaultSpace : OpenEventSpace { + ensureRepository + var lck = getLock + lck.readLock.lock + try { + return this.defaultSpaceInstance + } finally { + lck.readLock.unlock + } + } + + def getSpace(spaceUUID : UUID) : S with S extends Space { + var repo = ensureRepository + if (this.defaultSpaceID == spaceUUID) { + var lck = getLock + lck.readLock.lock + try { + return this.defaultSpaceInstance as S + } finally { + lck.readLock.unlock + } + } + var spaceID = new SpaceID(ID, spaceUUID, null) + return repo.getSpace(spaceID) as S + } + + def getSpaces(spec : Class>) : SynchronizedIterable with S extends Space { + ensureRepository.getSpaces(spec) + } + + def getSpaces : SynchronizedIterable { + ensureRepository.getSpaces + } + + def createSpace(spec : Class>, spaceUUID : UUID, + creationParams : Object*) : S with S extends Space { + // If the space identifier corresponds to the default space, get the default space (by accessing the cache attribute) + var instance = ensureRepository + if (spaceUUID == this.defaultSpaceID) { + var lck = getLock + lck.readLock.lock + try { + return this.defaultSpaceInstance as S + } finally { + lck.readLock.unlock + } + } + var spaceID = new SpaceID(ID, spaceUUID, spec) + return instance.createSpace(spaceID, spec, creationParams) + } + + def getOrCreateSpaceWithSpec(spec : Class>, spaceUUID : UUID, + creationParams : Object*) : S with S extends Space { + var instance = ensureRepository + var spaceID = new SpaceID(ID, spaceUUID, spec) + return instance.getOrCreateSpaceWithSpec(spaceID, spec, creationParams) + } + + def getOrCreateSpaceWithID(spec : Class>, spaceUUID : UUID, + creationParams : Object*) : S with S extends Space { + var instance = ensureRepository + if (this.defaultSpaceID == spaceUUID) { + var lck = getLock + lck.readLock.lock + try { + return this.defaultSpaceInstance as S + } finally { + lck.readLock.unlock + } + } + var spaceID = new SpaceID(ID, spaceUUID, spec) + return instance.getOrCreateSpaceWithID(spaceID, spec, creationParams) + } + + /** Add listener on the space repository changes. + * + * @param listener + */ + def addSpaceRepositoryListener(listener : SpaceRepositoryListener) { + ensureRepository.addSpaceRepositoryListener(listener) + } + /** Remove listener on the space repository changes. + * + * @param listener + */ + def removeSpaceRepositoryListener(listener : SpaceRepositoryListener) { + var instance : SpaceRepository + var lck = getLock + lck.readLock.lock + try { + instance = this.repositoryInstance + } finally { + lck.readLock.unlock + } + if (instance !== null) { + instance.removeSpaceRepositoryListener(listener) + } + } + +} + +/** + * Factory of SRE contexts. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.6.0 + */ +@ImplementedBy(typeof(DefaultContextFactory)) +interface ContextFactory { + + /** Create an instance of SRE context. + * + * @param contextID the identifier of the context. + * @param defaultSpaceID the identifier of the defualt space into the context. + * @param owner is the owner of the context. If {@code null}, the context is the root context. + */ + def newInstance(contextID : UUID, defaultSpaceID : UUID, owner : Agent) : Context + +} + +/** + * Factory of SRE contexts. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.6.0 + */ +class DefaultContextFactory implements ContextFactory { + + val lock : Provider + val spaceRepositoryProvider : Provider + val logger : LoggingService + val listenerFactory : Provider + + /** Constructor with injected parameters. + * + * @param id identifier of the context. + * @param defaultSpaceID identifier of the default space in the context. + * @param lock the synchronization lock to use. + * @param spaceRepositoryProvider the provider of space repository + * @param logger the logging service. + * @param listenerFactory the factory of space listeners. + */ + @Inject + new (lock : Provider, + spaceRepositoryProvider : Provider, logger : LoggingService, + @KernelScope listenerFactory : Provider) { + this.lock = lock + this.spaceRepositoryProvider = spaceRepositoryProvider + this.logger = logger + this.listenerFactory = listenerFactory + } + + override newInstance(contextID : UUID, defaultSpaceID : UUID, owner : Agent) : Context { + new Context(contextID, defaultSpaceID, owner, + this.lock.get, this.spaceRepositoryProvider, this.logger, this.listenerFactory.get) + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/ContextService.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/ContextService.sarl new file mode 100644 index 0000000000..7fb388e269 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/ContextService.sarl @@ -0,0 +1,184 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.context + +import com.google.common.util.concurrent.Service +import io.sarl.core.ContextJoined +import io.sarl.core.ContextLeft +import io.sarl.core.MemberJoined +import io.sarl.core.MemberLeft +import io.sarl.lang.core.Agent +import io.sarl.lang.core.AgentContext +import io.sarl.lang.core.SpaceID +import io.sarl.lang.util.SynchronizedIterable +import java.util.EventListener +import java.util.UUID +import java.util.concurrent.locks.ReadWriteLock +import javax.inject.Provider + +/** + * This service enables to store the contexts and to manage the spaces in the SRE platform. + * + * @author $Author: srodriguez$ + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +interface ContextService extends Service { + + /** + * Replies the root context. + * + * @return the root context. + */ + def getRootContext : Context + + /** + * Create a new context instance and do not register it into the repository. + * + * @param contextID the id of the context to create. + * @param defaultSpaceID the id of the default space of the context to create. + * @param owner is the owner of the context. If {@code null}, the context is the root context. + * @return the context. + */ + def createContextWithoutRegistration(contextID : UUID, defaultSpaceID : UUID, owner : Agent) : Context + + /** + * Create a new context instance and register it into the internal repository. + * + * @param contextID the id of the context to create. + * @param defaultSpaceID the id of the default space of the context to create. + * @param owner is the owner of the context. If {@code null}, the context is the root context. + * @return the context. + */ + def createContext(contextID : UUID, defaultSpaceID : UUID, owner : Agent) : Context + + /** + * Replies an existing context instance. + * + * @param contextID the id of the context to create + * @return the context, {@code null}. + */ + def getContext(contextID : UUID) : Context + /** + * Remove the context instance. + * + * @param contextID the id of the context to remove + * @return the removed context, {@code null}. + */ + def removeContext(contextID : UUID) : Context + + /** + * Replies all the contexts. + * + * @return a view on all the contexts. + * @since 0.10 + */ + def getAllContexts : SynchronizedIterable + +} + +/** + * Listener on events related to the members of an external context. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +interface ExternalContextMemberListener extends EventListener { + + /** + * Fires an {@link MemberJoined} event into the newly joined parent Context default space to notify other context's members + * that a new agent joined this context. + * + * @param joinedContext the newly joined context to notify its members + * @param joinedContextSpaceId the identifier of the default space inside {@code joinedContext}. + * @param joiningAgentID the identifier of the new member. + * @param joiningAgentType the name of the agent type that has joined the context. + */ + def memberJoined(joinedContext : AgentContext, joinedContextSpaceId : SpaceID, joiningAgentID : UUID, + joiningAgentType : String) + + /** + * Fires an {@link MemberLeft} event into the default space of the Context that will be left to notify other context's members + * that an agent has left this context. + * + * @param leftContext the context that will be left + * @param leftContextSpaceId the identifier of the default space inside {@code leftContext}. + * @param leftAgentID the identifier of the agent that has left the context. + * @param leftAgentType the name of the agent type that has left the context. + */ + def memberLeft(leftContext : AgentContext, leftContextSpaceId : SpaceID, leftMemberID : UUID, + agentType : String) + +} + +/** + * Listener on events related to the membership within a context. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +interface InternalContextMembershipListener extends EventListener { + + /** + * Fires an {@link ContextJoined} event into the Inner Context default space of the owner agent to notify behaviors/members + * that a new context has been joined. + * + * @param futureContext ID of the newly joined context + * @param futureContextDefaultSpaceID ID of the default space of the newly joined context + */ + def contextJoined(futureContext : UUID, futureContextDefaultSpaceID : UUID) + + /** + * Fires an {@link ContextLeft} event into the Inner Context Default space of the owner agent to notify behaviors/members that + * the specified context has been left. + * + * @param contextID the ID of context that will be left + */ + def contextLeft(contextID : UUID) + +} + +/** + * Factory of the listeners on events related to the membership within a context. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.7.0 + */ +interface InternalContextMembershipListenerFactory { + + /** Create a listener associated to the given agent. + * + * @param agent the agent to link to the provided listener. + * @param lockProvider a provider of synchronization locks. + * @return a listener. + */ + def create(owner : Agent, lockProvider : Provider) : InternalContextMembershipListener + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/LocalSpaceRepository.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/LocalSpaceRepository.sarl new file mode 100644 index 0000000000..ce3ee79aa1 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/LocalSpaceRepository.sarl @@ -0,0 +1,90 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.context + +import com.google.inject.Injector +import io.sarl.sre.boot.configs.SreConfig +import io.sarl.sre.services.executor.ExecutorService +import java.util.Map +import java.util.UUID +import javax.inject.Inject +import java.util.concurrent.locks.ReadWriteLock +import io.sarl.sre.internal.SmartListenerCollection +import javax.inject.Provider + +/** + * A repository of spaces specific to a given context. + * + *

This repository is thread-safe. + * + * @author $Author: ngaud$ + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class LocalSpaceRepository extends SpaceRepository { + + val spaces : Map + + /** Constructor. + * + * @param injector the injector. + * @param executor the asynchronous execution service. + * @param configFactory accessor to the configuration. + * @param listenerCollection the instance of the listener collection. + * @param lockProvider the provider of the locking objects that must be used to synchronize this repository. + */ + @Inject + new ( + injector : Injector, + executor : ExecutorService, + config : SreConfig, + listenerCollection : SmartListenerCollection, + lockProvider : Provider) { + this(injector, executor, config, null, listenerCollection, lockProvider) + } + + /** Constructor. + * + * @param injector the injector. + * @param executor the asynchronous execution service. + * @param config the configuration. + * @param listenerCollection the instance of the listener collection. + * @param lockProvider the provider of the locking objects that must be used to synchronize this repository. + */ + new (injector : Injector, executor : ExecutorService, config : SreConfig, + internalStructure : Map, listenerCollection : SmartListenerCollection, + lockProvider : Provider) { + super(injector, executor, config, listenerCollection, lockProvider) + if (internalStructure !== null) { + this.spaces = internalStructure + } else { + this.spaces = newTreeMap(null) + } + } + + + protected override getSharedStructure : Map { + this.spaces + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/MemoryBasedContextService.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/MemoryBasedContextService.sarl new file mode 100644 index 0000000000..8e41ac8506 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/MemoryBasedContextService.sarl @@ -0,0 +1,99 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.context + +import com.google.common.collect.Iterables +import com.google.inject.Injector +import io.sarl.lang.util.SynchronizedIterable +import io.sarl.sre.KernelScope +import io.sarl.util.concurrent.Collections3 +import java.util.Collections +import java.util.Map +import java.util.UUID +import java.util.concurrent.locks.ReadWriteLock +import javax.inject.Inject + +/** + * A service managing the root context and enabling the creation of contexts by injection into the + * local memory. + * The context repository is not shared among different instances of the SRE kernel. + * + *

This service is thread-safe. + * + * @author $Author: ngaud$ + * @author $Author: srodriguez$ + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class MemoryBasedContextService extends AbstractInjectionBasedContextService { + + var contextRepository : Map + + /** Constructor. + * + * @param rootContext the root context. + * @param injector is the injector. + * @param factory is the factory of contexts. + * @param lock is the lock that must be used by this service. + */ + @Inject + new(@KernelScope rootContext : Context, injector : Injector, factory : ContextFactory, lock : ReadWriteLock) { + super(rootContext, injector, factory, lock) + } + + protected def getContextInternalStructure : Map { + var repository : Map + var lck = getLock + lck.readLock.lock + try { + repository = this.contextRepository + } finally { + lck.readLock.unlock + } + if (repository === null) { + lck.writeLock.lock + try { + repository = this.contextRepository + if (repository === null) { + repository = newTreeMap(null) + this.contextRepository = repository + } + } finally { + lck.writeLock.unlock + } + } + return repository + } + + def getAllContexts : SynchronizedIterable { + var lck = getLock + lck.writeLock.lock + try { + var all = Iterables::concat(Collections::singletonList(rootContext), this.contextInternalStructure.values) + return Collections3::unmodifiableSynchronizedIterable(all, lck) + } finally { + lck.writeLock.unlock + } + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/Messages.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/Messages.sarl new file mode 100644 index 0000000000..3b3f0fcb6d --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/Messages.sarl @@ -0,0 +1,47 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.context + +import org.eclipse.osgi.util.NLS + +/** Messages. + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +final class Messages extends NLS { + + static val BUNDLE_NAME = typeof(Messages).getPackage.name + ".messages" + + static new { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, typeof(Messages)) + } + + public static var InjectionBasedContextService_0 : String + public static var InjectionBasedContextService_1 : String + + private new { + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/SpaceRepository.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/SpaceRepository.sarl new file mode 100644 index 0000000000..1d74ae0aa4 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/SpaceRepository.sarl @@ -0,0 +1,739 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.context + +import com.google.inject.AbstractModule +import com.google.inject.Injector +import com.google.inject.Key +import com.google.inject.Provides +import com.google.inject.name.Names +import io.sarl.core.OpenEventSpace +import io.sarl.core.OpenEventSpaceSpecification +import io.sarl.lang.annotation.PrivateAPI +import io.sarl.lang.core.EventSpace +import io.sarl.lang.core.EventSpaceSpecification +import io.sarl.lang.core.SREutils +import io.sarl.lang.core.Space +import io.sarl.lang.core.SpaceID +import io.sarl.lang.core.SpaceSpecification +import io.sarl.lang.util.SynchronizedIterable +import io.sarl.sre.KernelScope +import io.sarl.sre.boot.configs.SreConfig +import io.sarl.sre.internal.SmartListenerCollection +import io.sarl.sre.services.executor.ExecutorService +import io.sarl.sre.services.logging.LoggingService +import io.sarl.sre.spaces.AbstractSpace +import io.sarl.sre.spaces.SpaceListener +import io.sarl.sre.spaces.SpaceParticipantListenerFactory +import io.sarl.sre.spaces.SreEventSpaceSpecification +import io.sarl.sre.spaces.SreOpenEventSpaceSpecification +import io.sarl.sre.spaces.SreRestrictedAccessEventSpaceSpecification +import io.sarl.util.Comparators +import io.sarl.util.DefaultSpace +import io.sarl.util.RestrictedAccessEventSpaceSpecification +import io.sarl.util.concurrent.Collections3 +import java.io.Serializable +import java.lang.ref.WeakReference +import java.util.EventListener +import java.util.Map +import java.util.Set +import java.util.UUID +import java.util.concurrent.TimeUnit +import java.util.concurrent.locks.ReadWriteLock +import java.util.logging.Logger +import javax.inject.Inject +import javax.inject.Provider + +/** + * A repository of spaces specific to a given context. + * + *

This repository is thread-safe. + * + * @author $Author: ngaud$ + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +@SuppressWarnings("use_reserved_sarl_annotation") +@PrivateAPI(isCallerOnly = true) +abstract class SpaceRepository implements SpaceListener { + + val injector : Injector + + val executor : ExecutorService + + val destroyDelay : int + + val lockProvider : Provider + + val lock : ReadWriteLock + + val listeners : SmartListenerCollection + + val spacesBySpec = >, Set>newTreeMap( + Comparators::classComparator) + + /** Reference to the default space of the owning context. + * @since 0.10 + */ + var defaultSpace : WeakReference + + /** Constructor. + * + * @param injector the injector. + * @param executor the asynchronous execution service. + * @param config the configuration. + * @param listenerCollection the instance of the listener collection. + * @param lock the locking object that must be used to synchronize this repository. + */ + @Inject + new (injector : Injector, executor : ExecutorService, config : SreConfig, + listenerCollection : SmartListenerCollection, lockProvider : Provider) { + this.injector = injector + this.executor = executor + this.destroyDelay = config.services.executors.keepAliveDuration + this.listeners = listenerCollection + this.lockProvider = lockProvider + this.lock = lockProvider.get + } + + /** Replies the internal map that contains the spaces */ + protected abstract def getSharedStructure : Map + + /** Create a space description. + * + *

This function should be overridden by the sub-types. + * + * @param id the space identifier. + * @param space the space instance. + * @return the description. + */ + protected def createSpaceDescription(id : SpaceID, ^space : Space) : SpaceDescription { + new SpaceDescription(id, ^space, this.lockProvider.get) + } + + /** Replies the lock for changing the internal data structures, except for the destroyable spaces. */ + def getLock : ReadWriteLock { + this.lock + } + + /** Replies the space information associated to the space if already associated. + * + *

This function is not thread-safe. + * + * @param newDescription the description to associated to the space. + * @return the space description, or {@code null} + */ + protected def life(^space : Space, newDescription : SpaceDescription) : SpaceDescription { + if (^space instanceof AbstractSpace) { + if (newDescription !== null) { + SREutils::setSreSpecificData(^space, newDescription) + return newDescription + } + return SREutils::getSreSpecificData(^space, typeof(SpaceDescription)) + } + return null + } + + /** Replies the space information associated to the space identifier if already associated. + * + *

This function is not thread-safe. + * + * @param newDescription the description to associated to the space. + * @return the space description, or {@code null} + */ + protected def life(spaceId : SpaceID, newDescription : SpaceDescription) : SpaceDescription { + if (newDescription !== null) { + SREutils::setSreSpecificData(spaceId, newDescription) + return newDescription + } + return SREutils::getSreSpecificData(spaceId, typeof(SpaceDescription)) + } + + /** + * Create an instance of the default space if it was not created before. + * + * @param spaceID ID of the default space. + * @return the new space. + */ + package final def createDefaultSpace(spaceID : SpaceID) : OpenEventSpace { + var defaultSpace = getOrCreateSpaceWithID(spaceID, typeof(OpenEventSpaceSpecification)) + var lck = getLock + lck.writeLock.lock + try { + if (defaultSpace === null) { + this.defaultSpace = null + } else { + this.defaultSpace = new WeakReference(defaultSpace) + } + } finally { + lck.writeLock.unlock + } + return defaultSpace + } + + private def createSpaceFirstInstance(spec : Class>, spaceID : SpaceID, + creationParams : Object[]) : S with S extends Space { + assert spaceID.spaceSpecification === null || + spaceID.spaceSpecification == spec, "The specification type is invalid" + + var defaultSpace : OpenEventSpace + var lck = getLock + lck.readLock.lock + try { + defaultSpace = this.defaultSpace?.get + } finally { + lck.readLock.unlock + } + var spaceSpecificationInstance : SpaceSpecification + if (defaultSpace === null) { + // Usually, this case cover the creation of the default space within a context + spaceSpecificationInstance = this.injector.getInstance(Key.get(spec, typeof(KernelScope))) + } else { + // Usually, this case is for the creation of the spaces that are not the default space + val defaultSpaceInjectionModule = new JustInTimeDefaultSpaceInjectionModule(defaultSpace) + val defaultSpaceInjector = this.injector.createChildInjector(defaultSpaceInjectionModule) + spaceSpecificationInstance = defaultSpaceInjector.getInstance(spec) + } + + // Split the call to create() to let the JVM to create the "empty" array for creation parameters. + var ^space : S + if (creationParams !== null && creationParams.length > 0) { + ^space = spaceSpecificationInstance.create(spaceID, creationParams) + } else { + ^space = spaceSpecificationInstance.create(spaceID) + } + assert ^space !== null + + val id = ^space.spaceID + assert id !== null + val description = createSpaceDescription(id, ^space) + ^space.life(description) + id.life(description) + val spaces = sharedStructure + lck.writeLock.lock + try { + var list = this.spacesBySpec.get(id.spaceSpecification) + if (list === null) { + list = newTreeSet(null) + this.spacesBySpec.put(id.spaceSpecification, list) + } + list += description + } finally { + lck.writeLock.unlock + } + spaces.put(id.ID, description) + if (^space instanceof AbstractSpace) { + ^space.spaceListenerIfNone = this + } + ^space.fireSpaceAdded(true) + return ^space + } + + /** Synchronize the internal structures of SpaceRepository with the content of + * {@link #getSharedStructure()}. + * + *

This function is thread-safe. + * + *

Caution: Do not call this function if you are not certain it does the expected things. + */ + def refreshInternalStructures : void { + var lck = getLock + lck.writeLock.lock + try { + this.spacesBySpec.clear + for (description : this.sharedStructure.values) { + var ^space = description.^space + var spaceId = ^space.spaceID + ^space.life(description) + spaceId.life(description) + var list = this.spacesBySpec.get(spaceId.spaceSpecification) + if (list === null) { + list = newTreeSet(null) + this.spacesBySpec.put(spaceId.spaceSpecification, list) + } + list += description + if (^space instanceof AbstractSpace) { + ^space.spaceListenerIfNone = this + } + } + } finally { + lck.writeLock.unlock + } + } + + private def ensureSpaceInstance(description : SpaceDescription) : Space { + assert description !== null + if (description.getSpace === null) { + val id = description.spaceID + assert id !== null + val ^space = this.injector.getInstance(id.spaceSpecification).create(id) + assert ^space !== null + description.setSpace = ^space + ^space.life(description) + id.life(description) + ^space.fireSpaceAdded(false) + } + return description.getSpace + } + + /** + * Destroy this repository and release all the resources. + */ + final def destroy { + var lck = getLock + lck.writeLock.lock + try { + safeDestroy + } finally { + lck.writeLock.unlock + } + this.listeners.clear + } + + /** + * Destroy this repository and release all the resources inside a synchronized section. + */ + protected def safeDestroy { + val spaces = sharedStructure + var iterator = spaces.values.iterator + while (iterator.hasNext) { + var description = iterator.next + var ^space = description.^space + description.^space = null + ^space.life(null) + ^space.spaceID.life(null) + iterator.remove + } + this.spacesBySpec.clear + } + + /** + * Create a space. + * + * @param - the type of the space to reply. + * @param spaceID ID of the space. + * @param spec specification of the space. + * @param creationParams creation parameters. + * @return the new space, or null if the space already exists. + */ + def createSpace(spaceID : SpaceID, spec : Class>, + creationParams : Object*) : S with S extends Space { + assert spaceID.spaceSpecification == spec, "The space identifier does not contains the same specification as the one given to the creation function" + spec.createSpaceFirstInstance(spaceID, creationParams) + } + + /** + * Remove a space. + * + * @param space the space. + */ + def removeSpace(^space : Space) { + if (^space !== null) { + var spaceID = ^space.spaceID + var spaces = sharedStructure + assert spaces !== null + var description : SpaceDescription + var lck = getLock + lck.writeLock.lock + try { + description = spaces.remove(spaceID.ID) + if (description !== null) { + var cousins = this.spacesBySpec.get(spaceID.spaceSpecification) + if (cousins !== null) { + cousins.remove(description) + } + } + } finally { + lck.writeLock.unlock + } + ^space.life(null) + spaceID.life(null) + if (description !== null) { + description.^space = null + ^space.fireSpaceRemoved(true) + } + } + } + + /** + * Retrieve the first space of the given specification, or create a space if none. + * + * @param - the type of the space to reply. + * @param spaceID ID of the space (used only when creating a space). + * @param spec specification of the space. + * @param creationParams creation parameters (used only when creating a space). + * @return the new space. + */ + def getOrCreateSpaceWithSpec(spaceID : SpaceID, spec : Class>, + creationParams : Object*) : S with S extends Space { + var description : SpaceDescription = null + var lck = getLock + lck.readLock.lock + try { + var spaces = spacesBySpec.get(spec) + if (spaces !== null && !spaces.empty) { + var defaultSpace = this.defaultSpace?.get + var iter = spaces.iterator + if (defaultSpace === null) { + if (iter.hasNext) { + description = iter.next + } + } else { + val dfid = defaultSpace.spaceID + while (description === null && iter.hasNext) { + var desc = iter.next + if (dfid != desc.spaceID) { + description = desc + } + } + } + } + } finally { + lck.readLock.unlock + } + if (description === null) { + return spec.createSpaceFirstInstance(spaceID, creationParams) + } + return description.ensureSpaceInstance as S + } + + /** + * Retrieve the first space of the given identifier, or create a space if none. + * + * @param - the type of the space to reply. + * @param spaceID ID of the space. + * @param spec specification of the space. + * @param creationParams creation parameters (used only when creating a space). + * @return the new space. + */ + def getOrCreateSpaceWithID(spaceID : SpaceID, spec : Class>, + creationParams : Object*) : S with S extends Space { + val spaces = sharedStructure + var description : SpaceDescription + var lck = getLock + lck.readLock.lock + try { + description = spaces.get(spaceID.ID) + } finally { + lck.readLock.unlock + } + if (description === null) { + return spec.createSpaceFirstInstance(spaceID, creationParams) + } + return description.ensureSpaceInstance as S + } + + /** + * Returns the collection of all spaces stored in this repository. + * + * @return the collection of all spaces stored in this repository. + */ + def getSpaces : SynchronizedIterable { + val spaces = sharedStructure + var lck = getLock + lck.readLock.lock + try { + var translated = spaces.values.map[it.ensureSpaceInstance] + return Collections3::unmodifiableSynchronizedIterable(translated, lck) + } finally { + lck.readLock.unlock + } + } + + /** + * Returns the collection of all spaces with the specified {@link SpaceSpecification} stored in this repository. + * + * @param - type of the spaces to reply. + * @param spec the specification used to filter the set of stored spaces. + * @return the collection of all spaces with the specified {@link SpaceSpecification} stored in this repository + */ + def getSpaces(spec : Class>) : SynchronizedIterable with S extends Space { + var lck = getLock + lck.readLock.lock + try { + var spaces = this.spacesBySpec.get(spec) + if (spaces !== null) { + var mapped = spaces.map [it.ensureSpaceInstance as S] + return Collections3::unmodifiableSynchronizedIterable(mapped, lock) + } + } finally { + lck.readLock.unlock + } + return Collections3::emptySynchronizedSet + } + + /** + * Returns the first instance of a space with the specified SpaceID. + * + * @param spaceID the identifier to retrieve. + * @return the space instance of null if none. + */ + def getSpace(spaceID : SpaceID) : Space { + val spaces = sharedStructure + var lck = getLock + lck.readLock.lock + try { + var description = spaces.get(spaceID.ID) + if (description !== null) { + return description.ensureSpaceInstance + } + } finally { + lck.readLock.unlock + } + return null + } + + /** Add listener on the space repository changes. + * + * @param listener + */ + def addSpaceRepositoryListener(listener : SpaceRepositoryListener) { + this.listeners.add(typeof(SpaceRepositoryListener), listener) + } + + /** Remove listener on the space repository changes. + * + * @param listener + */ + def removeSpaceRepositoryListener(listener : SpaceRepositoryListener) { + this.listeners.remove(typeof(SpaceRepositoryListener), listener) + } + + /** + * Notifies the listeners on the space creation. + * + * @param space the created space. + * @param isLocalCreation indicates if the creation of the space was initiated on the current kernel. + */ + protected def fireSpaceAdded(^space : Space, isLocalCreation : boolean) { + this.listeners.notifyListeners(typeof(SpaceRepositoryListener)) [ + spaceCreated(^space, isLocalCreation) + ] + } + + /** + * Notifies the listeners on the space destruction. + * + * @param space the removed space. + * @param isLocalDestruction indicates if the destruction of the space was initiated on the current kernel. + */ + protected def fireSpaceRemoved(^space : Space, isLocalDestruction : boolean) { + this.listeners.notifyListeners(typeof(SpaceRepositoryListener)) [ + spaceDestroyed(^space, isLocalDestruction) + ] + } + + override destroyableSpace(^space : Space) { + var run : Runnable = [ + var participants = ^space.participants + var couldBeDestroyed : boolean + var lck = participants.lock + lck.readLock.lock + try { + couldBeDestroyed = participants.empty + } finally { + lck.readLock.unlock + } + if (couldBeDestroyed) { + removeSpace(^space) + } + ] + this.executor.schedule(this.destroyDelay, TimeUnit::SECONDS, run) + } + + /** + * Description of a space. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ + static class SpaceDescription implements Serializable, Comparable { + + val id : SpaceID + + transient var ^space : Space + + val lock : ReadWriteLock + + new (id : SpaceID, ^space : Space, lock : ReadWriteLock) { + this.id = id + this.^space = ^space + this.lock = lock + } + + def getSpaceID : SpaceID { + this.id + } + + def getSpace : Space { + this.lock.readLock.lock + try { + return this.^space + } finally { + this.lock.readLock.unlock + } + } + + def setSpace(^space : Space) { + this.lock.writeLock.lock + try { + this.^space = ^space + } finally { + this.lock.writeLock.unlock + } + } + + def equals(obj : Object) : boolean { + if (obj instanceof SpaceDescription) { + return this.id == obj.id + } + return false + } + + def hashCode : int { + this.id.hashCode + } + + def compareTo(other : SpaceDescription) : int { + if (other !== null) { + return this.id <=> other.id + } + return Integer::MIN_VALUE + } + + } + + /** + * An injection module that is able to inject the default space instance into another space implementation. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ + private static class JustInTimeDefaultSpaceInjectionModule extends AbstractModule { + + static val NAME = "defaultSpace" + + val defaultSpace : OpenEventSpace + + package new (defaultSpace : OpenEventSpace) { + assert defaultSpace !== null + this.defaultSpace = defaultSpace + } + + override configure { + typeof(OpenEventSpace).bind.annotatedWith(Names::named(NAME)).toInstance(this.defaultSpace) + typeof(OpenEventSpace).bind.annotatedWith(typeof(DefaultSpace)).toInstance(this.defaultSpace) + } + + /** This injection definition is for the spaces that are not a default space. + * + *

Note: This provider must be defined into this "temp" module because it depends on the + * definition of the default space injection. + */ + @Provides + def provideEventSpaceSpecification(injector : Injector, lockProvider : Provider, + @DefaultSpace defaultSpace : OpenEventSpace, + @KernelScope spaceParticipantListenerFactory : Provider, + logger : LoggingService) : EventSpaceSpecification { + new SreEventSpaceSpecification(injector, lockProvider, defaultSpace, spaceParticipantListenerFactory.get, + logger) + } + + /** This injection definition is for the spaces that are not a default space. + * + *

Note: This provider must be defined into this "temp" module because it depends on the + * definition of the default space injection. + */ + @Provides + def provideOpenEventSpaceSpecification(injector : Injector, lockProvider : Provider, + @DefaultSpace defaultSpace : OpenEventSpace, + @KernelScope spaceParticipantListenerFactory : Provider, + logger : LoggingService) : OpenEventSpaceSpecification { + new SreOpenEventSpaceSpecification(injector, lockProvider, defaultSpace, + spaceParticipantListenerFactory.get, logger) + } + + /** This injection definition is for the spaces that are not a default space. + * + *

Note: This provider must be defined into this "temp" module because it depends on the + * definition of the default space injection. + */ + @Provides + def provideRestrictedAccessEventSpaceSpecification(injector : Injector, lockProvider : Provider, + @DefaultSpace defaultSpace : OpenEventSpace, + @KernelScope spaceParticipantListenerFactory : Provider, + logger : LoggingService) : RestrictedAccessEventSpaceSpecification { + new SreRestrictedAccessEventSpaceSpecification(injector, lockProvider, defaultSpace, + spaceParticipantListenerFactory.get, logger) + } + + } + +} + +/** + * Listener on events related to the space repository. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +interface SpaceRepositoryListener extends EventListener { + + /** + * Invoked when the space is added. + * + * @param space reference to the created space. + * @param isLocalCreation indicates if the creation of the space was initiated on the current kernel. + */ + def spaceCreated(^space : Space, isLocalCreation : boolean) + + /** + * Invoked when the space is destroyed. + * + * @param space reference to the destroyed space. + * @param isLocalDestruction indicates if the destruction of the space was initiated on the current kernel. + */ + def spaceDestroyed(^space : Space, isLocalDestruction : boolean) + +} + +/** Factory for a space event emitter. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.7.0 + */ +interface SpaceRepositoryListenerFactory { + + def create(contextID : UUID, defaultSpace : EventSpace, logger : Logger) : SpaceRepositoryListener + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/messages.properties b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/messages.properties new file mode 100644 index 0000000000..7f19ac6077 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/context/messages.properties @@ -0,0 +1,2 @@ +InjectionBasedContextService_0=Context {0} was created +InjectionBasedContextService_1=Context {0} was destroyed diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/AbstractExecutorService.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/AbstractExecutorService.sarl new file mode 100644 index 0000000000..dae8e57871 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/AbstractExecutorService.sarl @@ -0,0 +1,239 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.executor + +import io.sarl.sre.services.AbstractSreService +import java.util.Collection +import java.util.LinkedList +import java.util.List +import java.util.concurrent.CountDownLatch +import java.util.concurrent.atomic.AtomicInteger +import java.util.function.Consumer +import java.util.logging.Logger +import java.util.stream.StreamSupport + +import static extension io.sarl.sre.services.executor.Runnables.* + +/** + * Abstract implementation for the services that executes the tasks. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.6.0 + */ +abstract class AbstractExecutorService extends AbstractSreService implements ExecutorService { + + val jreExecutor : java.util.concurrent.ExecutorService + + /** + * Constructor. + * + * @param service the JRE service. + */ + new (service : java.util.concurrent.ExecutorService) { + this.jreExecutor = service + } + + /** + * Replies the JRE service for scheduled tasks. + * + * @return the JRE service. + */ + def getExecutorService : java.util.concurrent.ExecutorService { + this.jreExecutor + } + + /** Execute the given tasks in parallel and wait for the termination. + * + * @param tasks the tasks to run. + * @param thrownExceptions indicates if the exceptions in the tasks are thrown into a combined exception. + * @param a callback for wrapping the tasks. + */ + protected def executeInThreadsAndWait(tasks : Collection, thrownExceptions : boolean, + wrapper : (boolean,Runnable)=>Runnable) { + var runExceptions : List = null + + if (tasks.size == 1) { + val runnable = tasks.head + assert runnable !== null + if (thrownExceptions) { + val finalRunExceptions = new LinkedList + runExceptions = finalRunExceptions + try { + // Catch the early-exit exception + wrapper.apply(false, runnable).run + } catch (e : Throwable) { + finalRunExceptions += e + } + } else { + wrapper.apply(true, runnable).run + } + } else { + val doneSignal = new CountDownLatch(tasks.size) + if (thrownExceptions) { + val finalRunExceptions = new LinkedList + runExceptions = finalRunExceptions + for (runnable : tasks) { + var wrunnable : Runnable = [ + try { + // Catch the early-exit exception + wrapper.apply(false, runnable).run + } catch (e : Throwable) { + synchronized (finalRunExceptions) { + finalRunExceptions += e + } + } finally { + doneSignal.countDown + } + ] + this.executorService.execute(wrunnable) + } + } else { + for (runnable : tasks) { + this.executorService.execute [ + try { + // Catch the early-exit exception + wrapper.apply(true, runnable).run + } finally { + doneSignal.countDown + } + ] + } + } + + // Wait + try { + doneSignal.await + } catch (ex : InterruptedException) { + // XXX: Improve because: + // This exception occurs when one of the launched task kills the agent before all the + // submitted tasks are finished. Keep in mind that killing an agent should kill the + // launched tasks. + // Example of code that is generating this issue: + // + // on Initialize { + // in (100) [ + // killMe + // ] + // } + // + // In this example, the killMe is launched before the Initialize code is finished; + // and because the Initialize event is fired through the current function, it + // causes the InterruptedException. + } + } + + // Re-throw the run-time exception + if (runExceptions !== null && !runExceptions.empty) { + var iterator = runExceptions.iterator + val firstException = iterator.next + while (iterator.hasNext) { + firstException.addSuppressed(iterator.next) + } + throw firstException + } + } + + /** Execute the given task in parallel and wait for the termination. + * + * @param task the task to run. + * @param nbExecutions number of runs + * @param runGroupSize the size of a parallel group + */ + protected final def executeInThreadsAndWait(task : SreRunnable, nbExecutions : int, + runGroupSize : int) : int { + assert runGroupSize >= 1 + val es = executorService + if (nbExecutions > 1) { + val errors = new AtomicInteger + var doneSignal : CountDownLatch + if (runGroupSize > 1) { + val numberOfGroups = nbExecutions / runGroupSize + val rest = nbExecutions - numberOfGroups * runGroupSize + if (rest > 0) { + doneSignal = new CountDownLatch(numberOfGroups + 1) + } else { + doneSignal = new CountDownLatch(numberOfGroups) + } + val finalSignal = doneSignal + for (var i = 0; i < numberOfGroups; i++) { + es.execute [ + try { + for (var j = 0; j < runGroupSize; j++) { + task.run + if (!task.success) { + errors.incrementAndGet + } + } + } finally { + finalSignal.countDown + } + ] + } + if (rest > 0) { + es.execute [ + try { + for (var j = 0; j < rest; j++) { + task.run + if (!task.success) { + errors.incrementAndGet + } + } + } finally { + finalSignal.countDown + } + ] + } + } else { + doneSignal = new CountDownLatch(nbExecutions) + val finalSignal = doneSignal + for (var i = 0; i < nbExecutions; i++) { + es.execute [ + try { + task.run + if (!task.success) { + errors.incrementAndGet + } + } finally { + finalSignal.countDown + } + ] + } + } + // Wait for all creators to complete before continuing + doneSignal.await + return nbExecutions - errors.get + } + if (nbExecutions == 1) { + task.run + return if (task.success) 1 else 0 + } + return 0 + } + + override applyBlockingConsumer(logger : Logger, collection : Iterable, task : Consumer) with T { + // Assuming that the task is properly synchronized on the collection. + StreamSupport::stream(collection.spliterator, true).forEach(task.protectConsumer(logger)) + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/EarlyExitException.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/EarlyExitException.sarl new file mode 100644 index 0000000000..c6f37bcf59 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/EarlyExitException.sarl @@ -0,0 +1,60 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.executor + +import java.util.logging.Logger + +import static extension io.sarl.sre.services.executor.Runnables.* + +/** + * This exception is thrown when a function must never return from the point of view of an agent. It permits to never return from + * functions such as {@code killMe}. + * + *

The executors are supposed to never log this function. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +final class EarlyExitException extends RuntimeException { + + val postCommand : Runnable + + /** + * Construct. + */ + package new (postCommand : Runnable) { + this.postCommand = postCommand + } + + /** Run any post treatment that is associated with the early-exist action. + * + * @param logger the logger to use for outputing the errors. + * @since 0.6.0 + */ + def runPostTreatment(logger : Logger) { + if (this.postCommand !== null) { + this.postCommand.protectRunnable(logger).run + } + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/ExecutorService.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/ExecutorService.sarl new file mode 100644 index 0000000000..b930ab80b6 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/ExecutorService.sarl @@ -0,0 +1,260 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.executor + +import com.google.common.util.concurrent.Service +import java.util.Collection +import java.util.concurrent.Callable +import java.util.concurrent.Future +import java.util.concurrent.ScheduledFuture +import java.util.concurrent.TimeUnit +import java.util.function.Consumer +import java.util.logging.Logger + +/** + * This class enables the SRE kernel to be distributed other a network. + * + * @author $Author: srodriguez$ + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +interface ExecutorService extends Service { + + /** This function simulate the never return from this call. + * The {@link EarlyExitException} is thrown. + * + * @param postCommand is the command to be run when the early-exit exception is catch. + * This command may be used for running post actions on the same thread as the one which + * does an early exit. + * @since 0.6.0 + */ + static def neverReturn(postCommand : Runnable = null) { + throw new EarlyExitException(postCommand) + } + + /** + * Submit a task to the executor service. + * + *

According to the implementation of the service, the given task may be run in the same or separated thread + * than the one of the caller. In another words, there is no warranty that the caller's thread is blocked until + * the termination of the task. + * + *

If an exception occurs into the given task, the exception is logged. It is never thrown by this function. + * + * @param logger the logger to use for errors. + * @param task the task to submit. + * @return a Future representing the pending execution task. + * @see #execute(Runnable) if you don't want a future. + */ + def executeAsap(logger : Logger = null, task : Runnable) : Future + + /** + * Submit a task to the executor service. The Future's get method will return the given result upon successful completion. + * + *

According to the implementation of the service, the given task may be run in the same or separated thread + * than the one of the caller. In another words, there is no warranty that the caller's thread is blocked until + * the termination of the task. + * + *

If an exception occurs into the given task, the exception is logged. It is never thrown by this function. + * + * @param - the type of the value replied by the task. + * @param task the task to submit. + * @param result result to return after the execution. + * @return a Future representing the pending execution task. + */ + def executeAsap(logger : Logger = null, result : T, task : Runnable) : Future with T + + /** + * Submit a task to the executor service. + * + *

According to the implementation of the service, the given task may be run in the same or separated thread + * than the one of the caller. In another words, there is no warranty that the caller's thread is blocked until + * the termination of the task. + * + *

If an exception occurs into the given task, the exception is logged. It is never thrown by this function. + * + * @param - the type of the value replied by the task. + * @param task the task to submit. + * @return a Future representing the pending execution task. + */ + def executeAsap(logger : Logger = null, task : Callable) : Future with T + + /** + * Schedule the given task. + * + *

According to the implementation of the service, the given task may be run in the same or separated thread + * than the one of the caller. In another words, there is no warranty that the caller's thread is blocked until + * the termination of the task. + * + *

If an exception occurs into the given task, the exception is logged. It is never thrown by this function. + * + * @param logger the logger to use for errors. + * @param command task to run + * @param delay delay for waiting before launching the command + * @param unit time unit of the delay + * @return a Future representing the pending execution task. + */ + def schedule(logger : Logger = null, delay : long = 1, unit : TimeUnit = null, command : Runnable) : ScheduledFuture + + /** + * Schedule the given task. + * + *

According to the implementation of the service, the given task may be run in the same or separated thread + * than the one of the caller. In another words, there is no warranty that the caller's thread is blocked until + * the termination of the task. + * + *

If an exception occurs into the given task, the exception is logged. It is never thrown by this function. + * + * @param - the type of the value replied by the task. + * @param logger the logger to use for errors. + * @param command task to run + * @param delay delay for waiting before launching the command + * @param unit time unit of the delay + * @return a Future representing the pending execution task. + */ + def schedule(logger : Logger = null, delay : long = 1, unit : TimeUnit = null, command : Callable) : ScheduledFuture with T + + /** + * Creates and executes a periodic action that becomes enabled first after the given initial delay, and subsequently with the + * given period; that is executions will commence after initialDelay then initialDelay+period, then initialDelay + 2 * period, + * and so on. If any execution of the task encounters an exception, subsequent executions are suppressed. Otherwise, the task + * will only terminate via cancellation or termination of the executor. If any execution of this task takes longer than its + * period, then subsequent executions may start late, but will not concurrently execute. + * + *

According to the implementation of the service, the given task may be run in the same or separated thread + * than the one of the caller. In another words, there is no warranty that the caller's thread is blocked until + * the termination of the task. + * + *

If an exception occurs into the given task, the exception is logged. It is never thrown by this function. + * + * @param logger the logger to use for errors. + * @param command task to run + * @param initialDelay the time to delay first execution + * @param period the period between successive executions + * @param unit è the time unit of the initialDelay and period parameters + * @return a Future representing the pending execution task. + */ + def scheduleAtFixedRate(logger : Logger = null, initialDelay : long = 0, period : long = 1, + unit : TimeUnit = null, command : Runnable) : ScheduledFuture + + /** + * Creates and executes a periodic action that becomes enabled first after the given initial delay, and subsequently with the + * given delay between the termination of one execution and the commencement of the next. If any execution of the task + * encounters an exception, subsequent executions are suppressed. Otherwise, the task will only terminate via cancellation or + * termination of the executor. + * + *

According to the implementation of the service, the given task may be run in the same or separated thread + * than the one of the caller. In another words, there is no warranty that the caller's thread is blocked until + * the termination of the task. + * + *

If an exception occurs into the given task, the exception is logged. It is never thrown by this function. + * + * @param logger the logger to use for errors. + * @param command the task to execute + * @param initialDelay the time to delay first execution + * @param delay the delay between the termination of one execution and the start of the next + * @param unit the time unit of the initialDelay and delay parameters + * @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon + * cancellation + */ + def scheduleWithFixedDelay(logger : Logger = null, initialDelay : long = 0, delay : long = 1, + unit : TimeUnit = null, command : Runnable) : ScheduledFuture + + /** + * Remove any canceled/terminated tasks from the lists of tasks. + */ + def purge + + /** + * Submit tasks to the executor service and wait for the termination of all the tasks. + * This function ensures that the caller's thread is blocked until all the given tasks have been finished. + * + *

According to the implementation of the service, the given tasks may be run in the same or separated thread + * than the one of the caller. + * + *

If an exception occurs into the given task, the exception is thrown if {@code thrownExceptions} + * evaluates to {@code true}. If it is evaluates to {@code false}, the exception is logged. + * + * @param task the task to submit. + * @param thrownExceptions indicates if the exceptions in the given tasks are thrown forward by this function. + * @param logger the logger to use for errors. + * @since 0.6.0 + */ + def executeBlockingTasks(logger : Logger = null, thrownExceptions : boolean = false, task : Collection) + + /** + * Submit a task on the collection's elements to the executor service and wait for the termination of all the tasks. + * + *

You should ensure that the given task is properly synchronized on any shared object. + * + *

According to the implementation of the service, the given task may be run in the same or separated thread + * than the one of the caller. + * + *

If an exception occurs into the given consume, the exception is logged and it is thrown. + * + * @param logger the logger to use for errors. + * @param collection the collection of elements to iterate on. + * @param task the task to submit. Be sure that the task is synchronized on the given collection. + * @since 0.6.0 + */ + def applyBlockingConsumer(logger : Logger = null, collection : Iterable, task : Consumer) with T + + /** + * Submit a single task multiple times to the executor service. + * + *

The same task instance will be submit to the executor service. + * + *

{@code runGroupSize} indicates how many number of times the task will be run on + * a single thread. + * + *

This function is equivalent to: + *


+	 * for(i in [ 1 .. (nbExecutions/runGroupSize) ])
+	 * do
+	 * execute({
+	 * for(j in [1..runGroupSize]) {
+	 * task.run
+	 * }
+	 * })
+	 * done
+	 * 
+ * + *

Caution: if a {@code task} is failing, the exception will be output on the logger. This function never fails. + * + *

According to the implementation of the service, the given task may be run in the same or separated thread + * than the one of the caller. + * + *

If an exception occurs into the given consume, the exception is logged. It is never thrown by this function. + * + * @param logger the logger to use for errors. + * @param task the task to submit. + * @param nbExecutions the number of times the task must be run, usually greater than 1. + * @param runGroupSize the number of tasks to be run by a single thread. + * @return the number of successful runs. + * @throws InterruptedException when the function cannot wait for task termination. + * @since 0.5.0 + */ + def executeBlockingTask(logger : Logger = null, nbExecutions : int, runGroupSize : int, + task : Runnable) : int + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/JreExecutorService.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/JreExecutorService.sarl new file mode 100644 index 0000000000..5ed36685b5 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/JreExecutorService.sarl @@ -0,0 +1,289 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.executor + +import io.sarl.sre.boot.configs.SreConfig +import io.sarl.sre.boot.configs.subconfigs.ExecutorsConfig +import io.sarl.sre.services.PreReleasableService +import java.lang.Thread.UncaughtExceptionHandler +import java.lang.ref.WeakReference +import java.util.Collection +import java.util.concurrent.Callable +import java.util.concurrent.Future +import java.util.concurrent.ScheduledExecutorService +import java.util.concurrent.ScheduledFuture +import java.util.concurrent.ThreadPoolExecutor +import java.util.concurrent.TimeUnit +import java.util.logging.Logger +import javax.inject.Inject +import javax.inject.Singleton + +import static extension io.sarl.sre.services.executor.Runnables.* + +/** + * Service that executes the tasks asynchronously (in threads) with the JRE executor service. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +@Singleton +class JreExecutorService extends AbstractExecutorService implements PreReleasableService { + + static val DEFAULT_DELAY = 1 + + val jreSchedules : ScheduledExecutorService + + var uncaughtExceptionHandler : UncaughtExceptionHandler + + var purgeTask : ScheduledFuture + + var configuration : ExecutorsConfig + + /** + * Constructor. + * + * @param standardService the JRE service for executing not scheduled tasks. + * @param scheduledService the JRE service for executing scheduled tasks. + */ + @Inject + new (standardService : java.util.concurrent.ExecutorService, scheduledService : ScheduledExecutorService) { + super(standardService) + this.jreSchedules = scheduledService + } + + /** + * Replies the JRE service for scheduled tasks. + * + * @param the JRE service. + */ + def getScheduledExecutorService : ScheduledExecutorService { + this.jreSchedules + } + + /** + * Change the default exception handler. + * + * @param handler the default exception handler. + */ + @Inject + def setUncaughtExceptionHandler(handler : UncaughtExceptionHandler) { + this.uncaughtExceptionHandler = handler + } + /** + * Replies the default exception handler. + * + * @return the default exception handler. + */ + def getUncaughtExceptionHandler : UncaughtExceptionHandler { + this.uncaughtExceptionHandler + } + + /** + * Change the configuration from the general configuration. + * + * @param config the configuration. + */ + @Inject + final def setConfiguration(config : SreConfig) { + var ecfg : ExecutorsConfig = null + if (config !== null) { + ecfg = config.services.executors + } + setConfiguration(ecfg) + } + + /** + * Change the configuration from the general configuration. + * + * @param config the configuration. + */ + def setConfiguration(config : ExecutorsConfig) { + this.configuration = config + } + + + /** + * Replies the configuration. + * + * @param the configuration. + */ + def getConfiguration : ExecutorsConfig { + this.configuration + } + + @SuppressWarnings("discouraged_reference") + protected def onStart { + assert this.jreSchedules !== null + var uncaughtHandler = getUncaughtExceptionHandler + if (uncaughtHandler !== null) { + Thread::setDefaultUncaughtExceptionHandler(uncaughtHandler) + } + // Launch a periodic task that is purging the executor pools. + var es = executorService + var ses = scheduledExecutorService + if (es instanceof ThreadPoolExecutor || ses instanceof ThreadPoolExecutor) { + var cfg = getConfiguration + val delay = if (cfg !== null) cfg.timeout * 2 else DEFAULT_DELAY + this.purgeTask = scheduledExecutorService.scheduleWithFixedDelay(new Purger(this), + delay, delay, TimeUnit::SECONDS) + } + } + + override onPreStop { + shutdown + } + + protected def onStop { + shutdown + } + + private def shutdown { + var pt = this.purgeTask + this.purgeTask = null + if (pt !== null) { + pt.cancel(true) + } + var je = getExecutorService + try { + je.shutdownNow + } catch (e : Throwable) { + } + var js = scheduledExecutorService + try { + js.shutdownNow + } catch (e : InterruptedException) { + } + } + + def executeBlockingTasks(logger : Logger, thrownExceptions : boolean = false, + tasks : Collection) { + tasks.executeInThreadsAndWait(thrownExceptions) [catchErrors, runnable | + if(catchErrors) runnable.protectRunnable(logger) else runnable.protectRunnable(null) + ] + } + + def executeBlockingTask(logger : Logger, + nbExecutions : int, runGroupSize : int, task : Runnable) : int { + executeInThreadsAndWait(task.protectRunnable(logger), nbExecutions, runGroupSize) + } + + def executeAsap(logger : Logger, task : Runnable) : Future { + executorService.submit(task.protectRunnable(logger)) + } + + def executeAsap(logger : Logger, result : T, task : Runnable) : Future with T { + executorService.submit(task.protectRunnable(logger), result) + } + + def executeAsap(logger : Logger, task : Callable) : Future with T { + executorService.submit(task.protectCallable(logger)) + } + + def schedule(logger : Logger, delay : long, unit : TimeUnit, command : Runnable) : ScheduledFuture { + scheduledExecutorService.schedule(command.protectRunnable(logger), delay, unit) + } + + def schedule(logger : Logger, delay : long, unit : TimeUnit, command : Callable) : ScheduledFuture with T { + scheduledExecutorService.schedule(command.protectCallable(logger), delay, unit) + } + + def scheduleAtFixedRate(logger : Logger, initialDelay : long, period : long, + unit : TimeUnit, command : Runnable) : ScheduledFuture { + scheduledExecutorService.scheduleAtFixedRate(command.protectRunnable(logger), initialDelay, period, unit) + } + + def scheduleWithFixedDelay(logger : Logger, initialDelay : long, delay : long, + unit : TimeUnit, command : Runnable) : ScheduledFuture { + scheduledExecutorService.scheduleWithFixedDelay(command.protectRunnable(logger), initialDelay, delay, unit) + } + + def purge { + var es = executorService + if (es instanceof ThreadPoolExecutor) { + es.purge + } + var ses = scheduledExecutorService + if (ses instanceof ThreadPoolExecutor) { + ses.purge + } + } + + /** + * Task that is purging the thread pools. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ + private static class Purger implements Runnable { + + val owner : WeakReference + + var oldThreadName : String + + new (owner : ExecutorService) { + this.owner = new WeakReference(owner) + } + + @SuppressWarnings("discouraged_reference") + private def setName : boolean { + if (!this.oldThreadName.nullOrEmpty) { + return false + } + val t = Thread::currentThread + this.oldThreadName = t.getName + t.name = toString + return true + } + + @SuppressWarnings("discouraged_reference") + private def restoreName : boolean { + if (this.oldThreadName.nullOrEmpty) { + return false + } + val t = Thread::currentThread + t.name = this.oldThreadName + this.oldThreadName = null + return true + } + + override run { + assert setName + try { + val owner = this.owner.get + if (owner !== null) { + owner.purge + } + } finally { + assert restoreName + } + } + + def toString : String { + "SARL Thread Purger" + } + + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/Messages.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/Messages.sarl new file mode 100644 index 0000000000..4f1209ff92 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/Messages.sarl @@ -0,0 +1,49 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.executor + +import org.eclipse.osgi.util.NLS + +/** Messages. + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +final class Messages extends NLS { + + static val BUNDLE_NAME = typeof(Messages).getPackage.name + ".messages" + + static new { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, typeof(Messages)) + } + + public static var AbortPolicy_0 : String + public static var AbortPolicy_1 : String + public static var AbortPolicy_2 : String + public static var AbortPolicy_3 : String + + private new { + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/Policies.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/Policies.sarl new file mode 100644 index 0000000000..6ff42648ff --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/Policies.sarl @@ -0,0 +1,195 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.executor + +import io.sarl.sre.services.logging.LoggingService +import java.lang.Thread.UncaughtExceptionHandler +import java.text.MessageFormat +import java.util.concurrent.CancellationException +import java.util.concurrent.RejectedExecutionHandler +import java.util.concurrent.ThreadPoolExecutor +import java.util.logging.Level +import java.util.logging.LogRecord +import javax.inject.Singleton + +/** + * Abstract implementation of the thread executor policies for rejected tasks and + * uncaught exceptions. + * + *

The handler for rejected tasks runs the rejected task + * directly in the calling thread of the {@code execute} method, + * unless the executor has been shut down, in which case the task + * is discarded (see {@link ThreadPoolExecutor.CallerRunsPolicy}). + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @see {@link ThreadPoolExecutor.CallerRunsPolicy} + */ +@Singleton +abstract class AbstractThreadExecutorPolicy implements RejectedExecutionHandler, UncaughtExceptionHandler { + + /** + * Run the given task within the current thread if the executor is not shut down. + * The task is not run by the given executor. The executor is used for checking if + * the executor service is shut down. + * + * @param runnable the runnable task to be executed + * @param executor the executor attempting to give the shut down status + * @return {@code true} if the task is run. {@code false} if the task was not run. + * @see {@link ThreadPoolExecutor.CallerRunsPolicy} + */ + protected static def runRejectedTask(runnable : Runnable, executor : ThreadPoolExecutor) : boolean { + // Runs the task directly in the calling thread of the {@code execute} method, + // unless the executor has been shut down, in which case the task + // is discarded. + if (!executor.isShutdown) { + runnable.run + return true + } + return false + } + + override rejectedExecution(runnable : Runnable, executor : ThreadPoolExecutor) { + runRejectedTask(runnable, executor) + } + +} + +/** + * A handler for rejected tasks that log a warning on the platform logger. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +@Singleton +abstract class AbstractLoggingThreadExecutorPolicy extends AbstractThreadExecutorPolicy { + + protected val logger : LoggingService + + /** + * @param logger the logging service that must be used for output the errors. + */ + new (logger : LoggingService) { + assert logger !== null + this.logger = logger + } + + /** Log the given error. + * + * @param exception the error. + * @param taskId the identifier of the task. + * @param taslName the name of the task. + */ + protected final def log(exception : Throwable, taskId : String, taskName : String) { + assert exception !== null + if (exception instanceof EarlyExitException) { + return + } + var cause = exception + while (cause.cause !== null && cause.cause !== cause) { + cause = cause.cause + } + var record : LogRecord + if (cause instanceof EarlyExitException) { + return + } + if (cause instanceof CancellationException || exception instanceof CancellationException) { + // Avoid too much processing if the error could not be logged + if (!this.logger.kernelLogger.isLoggable(Level::FINEST)) { + return + } + record = new LogRecord(Level::FINEST, MessageFormat::format(Messages::AbortPolicy_1, taskId, taskName)) + } else if (cause instanceof InterruptedException || exception instanceof InterruptedException) { + // Avoid too much processing if the error could not be logged + if (!this.logger.kernelLogger.isLoggable(Level::FINEST)) { + return + } + record = new LogRecord(Level::FINEST, MessageFormat::format(Messages::AbortPolicy_2, taskId, taskName)) + } else { + // Avoid too much processing if the error could not be logged + if (!this.logger.kernelLogger.isLoggable(Level::SEVERE)) { + return + } + record = new LogRecord(Level::SEVERE, + MessageFormat::format(Messages::AbortPolicy_3, cause.localizedMessage, taskId, taskName)) + } + + record.setThrown(cause) + + val trace = cause.stackTrace + if (trace !== null && trace.length > 0) { + val elt = trace.get(0) + assert elt !== null + record.sourceClassName = elt.className + record.sourceMethodName = elt.methodName + } + + this.logger.kernelLogger.log(record) + } + +} + +/** + * A handler for rejected tasks and uncaught exceptions that logs a warning on the platform logger + * when a task is rejected, and an error for each uncaught exception. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @see {@link ThreadPoolExecutor.CallerRunsPolicy} + */ +@Singleton +class VerboseThreadExecutorPolicy extends AbstractLoggingThreadExecutorPolicy { + + override rejectedExecution(runnable : Runnable, executor : ThreadPoolExecutor) { + if (!runRejectedTask(runnable, executor)) { + val record = new LogRecord(Level::FINE, MessageFormat::format(Messages::AbortPolicy_0, runnable.toString)) + this.logger.kernelLogger.log(record) + } + } + + @SuppressWarnings("discouraged_reference") + override uncaughtException(t : Thread, e : Throwable) { + e.log(Long::toString(t.id), t.name) + } + +} + +/** + * A handler for rejected tasks and uncaught exceptions that logs nothing. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +@Singleton +class QuietThreadExecutorPolicy extends AbstractThreadExecutorPolicy { + + override uncaughtException(t : Thread, e : Throwable) { + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/Runnables.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/Runnables.sarl new file mode 100644 index 0000000000..8e55aa96cc --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/Runnables.sarl @@ -0,0 +1,95 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.executor + +import java.util.concurrent.Callable +import java.util.function.Consumer +import java.util.logging.Logger + +/** + * Utilities for runnables and callables. + * + * @param the type of the result. + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +final class Runnables { + + private new { + } + + /** Create a task with the given runnable. + * + * @param runnable the runnable. + * @param logger the logger to use. + * @return the task. + */ + static def protectRunnable(runnable : Runnable, logger : Logger) : SreRunnable { + if (runnable instanceof SreRunnable) { + return runnable + } + return new SreRunnable(runnable, logger) + } + + + /** Create a task with the given callable. + * + * @param the type of the returned value. + * @param callable the callable. + * @param logger the logger to use. + * @return the task. + */ + static def protectCallable(callable : Callable, logger : Logger) : SreCallable with T { + if (callable instanceof SreCallable) { + return callable as SreCallable + } + return new SreCallable(callable, logger) + } + + /** Create a task with the given consumer. + * + * @param the type of the returned value. + * @param consumer the consumer. + * @param logger the logger to use. + * @return the task. + */ + static def protectConsumer(consumer : Consumer, logger : Logger) : SreConsumer with T { + if (consumer instanceof SreConsumer) { + return consumer as SreConsumer + } + return new SreConsumer(consumer, logger) + } + + /** Create a wrapper with a specific thread name. + * + * @param runnable the task to wrap. + * @param name the new name of the associated, if not {@code null}. + * @return the wrapping task. + * @since 0.10 + */ + static def named(runnable : Runnable, name : String) : Runnable { + new SreKernelRunnable(runnable, name) + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/WrappedRunnables.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/WrappedRunnables.sarl new file mode 100644 index 0000000000..0874a33fa2 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/WrappedRunnables.sarl @@ -0,0 +1,440 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.executor + +import java.util.concurrent.Callable +import java.util.function.Consumer +import java.util.logging.Level +import java.util.logging.Logger +import org.eclipse.xtend.lib.annotations.Accessors + +/** + * Abstract implementation of a task that could be run on the JRE executor service. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.6 + * @see Runnables + */ +abstract class SreExecutable { + + val logger : Logger + + /** + * @param logger the logger to use. + */ + protected new (logger : Logger) { + this.logger = logger + } + + /** Replies the logger. + * + * @return the logger, or {@code null} if the platform logger should be used. + */ + def getLogger : Logger { + this.logger + } + + /** Replies a not-empty message for the given exception. + * + * @param ex the exception. + * @return the error message. + */ + protected static def exceptionMessage(ex : Throwable) { + if (ex !== null) { + val msg = ex.localizedMessage + if (!msg.nullOrEmpty) { + return msg + } + } + var msg = ex.class.simpleName + return msg + } + + /** Replies the root cause of the given exception. + * + * @param ex the exception for which the root cause msut be determined. + * @return the root cause, never null. + */ + protected static def getRootCause(ex : Throwable) : Throwable { + assert ex !== null + var root = ex + while (root.cause !== null && root.cause !== root && root.cause !== ex) { + root = root.cause + } + return root + } + +} + +/** + * A specific SRE runnable that is catching the {@link EarlyExitException}. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.6 + * @see Runnables + */ +class SreRunnable extends SreExecutable implements Runnable { + + val runnable : Runnable + var success = false + + /** + * @param runnable the wrapped task. + * @param logger the logger to use. + */ + protected new (runnable : Runnable, logger : Logger) { + super(logger) + assert runnable !== null + this.runnable = runnable + } + + /** + * @param logger the logger to use. + */ + protected new (logger : Logger) { + super(logger) + this.runnable = null + } + + /** Replies the wrapped task. + * + * @return the runnable. + */ + def getWrappedRunnable : Runnable { + this.runnable + } + + def run { + runWithEarlyExitSupport + } + + private def protectRun(runnable : Runnable) { + try { + runnable?.run + } catch (ex : EarlyExitException) { + ex.runPostTreatment(this.logger) + } catch (ex : InterruptedException) { + // Ignore this exception + } + } + + /** Run the wrapped task with the early exist support. + * The {@link EarlyExitException} is silently catch. + */ + protected final def runWithEarlyExitSupport { + if (this.logger !== null) { + try { + this.success = false + this.runnable.protectRun + this.success = true + } catch (ex : Throwable) { + val cause = ex.rootCause + this.logger.log(Level::SEVERE, cause.exceptionMessage, cause) + } + } else { + this.runnable.protectRun + this.success = true + } + } + + /** Replies if the task was finished on a success. + * + * @return {@code true} if the tas if finished and successful. {@code false} + * if the task is not not finished or successful. + */ + def isSuccess : boolean { + this.success + } + + def toString : String { + this.runnable?.toString + } + + def equals(obj : Object) : boolean { + this.runnable == obj + } + + def hashCode : int { + if (this.runnable !== null) this.runnable.hashCode else 0 + } + +} + +/** + * A specific SRE callable that is catching the {@link EarlyExitException}. + * + * @param the type of the result. + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.6 + * @see Runnables + */ +class SreCallable extends SreExecutable implements Callable { + + val callable : Callable + + var success = false + + /** + * @param callable the wrapped task. + * @param logger the logger to use. + */ + protected new (callable : Callable, logger : Logger) { + super(logger) + assert callable !== null + this.callable = callable + } + + /** + * @param logger the logger to use. + */ + protected new (logger : Logger) { + super(logger) + this.callable = null + } + + /** Replies the wrapped task. + * + * @return the callable. + */ + def getWrappedCallable : Callable { + this.callable + } + + def call : T { + callWithEarlyExitSupport + } + + private def protectCall(callable : Callable) : T with T { + try { + return callable?.call + } catch (ex : EarlyExitException) { + ex.runPostTreatment(this.logger) + return null + } catch (ex : InterruptedException) { + // Ignore this exception + return null + } + } + + /** Run the wrapped task with the early exist support. + * The {@link EarlyExitException} is silently catched. + * + * @return the computed value. + * @throws Exception the error in the wrapped task, exit {@link EarlyExitException} + */ + protected final def callWithEarlyExitSupport : T { + if (this.logger !== null) { + try { + this.success = false + var value = this.callable.protectCall + this.success = true + return value + } catch (ex : Throwable) { + val cause = ex.rootCause + this.logger.log(Level::SEVERE, cause.exceptionMessage, cause) + return null + } + } else { + var value = this.callable.protectCall + this.success = true + return value + } + } + + /** Replies if the task was finished on a success. + * + * @return {@code true} if the tas if finished and successful. {@code false} + * if the task is not not finished or successful. + */ + def isSuccess : boolean { + this.success + } + + def toString : String { + this.callable?.toString + } + + def equals(obj : Object) : boolean { + this.callable == obj + } + + def hashCode : int { + if (this.callable !== null) this.callable.hashCode else 0 + } + +} + +/** + * A specific SRE consumer that is catching the {@link EarlyExitException}. + * + * @param the type of the result. + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.7 + * @see Runnables + */ +class SreConsumer extends SreExecutable implements Consumer { + + val consumer : Consumer + + var success = false + + /** + * @param consumer the wrapped task. + * @param logger the logger to use. + */ + protected new (consumer : Consumer, logger : Logger) { + super(logger) + assert consumer !== null + this.consumer = consumer + } + + /** + * @param logger the logger to use. + */ + protected new (logger : Logger) { + super(logger) + this.consumer = null + } + + /** Replies the wrapped task. + * + * @return the consumer. + */ + def getWrappedConsumer : Consumer { + this.consumer + } + + def accept(t : T) { + t.consumeWithEarlyExitSupport + } + + private def protectAccept(consumer : Consumer, element : T) : void with T { + try { + consumer?.accept(element) + } catch (ex : Throwable) { + val cause = ex.rootCause + if (cause instanceof EarlyExitException) { + cause.runPostTreatment(this.logger) + } + else if (cause instanceof InterruptedException) { + // Ignore this exception + } else { + throw cause + } + } + } + /** Run the wrapped task with the early exist support. + * The {@link EarlyExitException} is silently catch. + * + * @param element the consumed element. + * @throws Exception the error in the wrapped task, exit {@link EarlyExitException} + */ + protected final def consumeWithEarlyExitSupport(element : T) : void { + if (this.logger !== null) { + try { + this.success = false + protectAccept(this.consumer, element) + this.success = true + } catch (ex : Throwable) { + val cause = ex.rootCause + this.logger.log(Level::SEVERE, cause.exceptionMessage, cause) + throw cause + } + } else { + protectAccept(this.consumer, element) + this.success = true + } + } + + /** Replies if the task was finished on a success. + * + * @return {@code true} if the task if finished and successful. {@code false} + * if the task is not not finished or successful. + */ + def isSuccess : boolean { + this.success + } + + def toString : String { + this.consumer?.toString + } + + def equals(obj : Object) : boolean { + this.consumer == obj + } + + def hashCode : int { + if (this.consumer !== null) this.consumer.hashCode else 0 + } + +} + +/** + * A specific SRE runnable for kernel tasks. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + * @see Runnables + */ +class SreKernelRunnable implements Runnable { + + @Accessors(PUBLIC_GETTER) + val source : Runnable + + @Accessors(PUBLIC_GETTER) + val name : String + + protected new (source : Runnable, name : String) { + assert source !== null + this.source = source + this.name = name + } + + @SuppressWarnings("discouraged_reference") + override run { + val th = Thread::currentThread + val oldName = th.name + try { + if (this.name !== null) { + th.name = this.name + } + this.source.run + } finally { + th.name = oldName + } + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/messages.properties b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/messages.properties new file mode 100644 index 0000000000..5b98dc5611 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/executor/messages.properties @@ -0,0 +1,4 @@ +AbortPolicy_0=Rejected task due to lack of resources or service shutdown: {0} +AbortPolicy_1=Task with the id ''{0}'' and the name ''{1}'' was cancelled. +AbortPolicy_2=Task with the id ''{0}'' and the name ''{1}'' was interrupted. +AbortPolicy_3=Uncaught exception: {0}\nin thread \#{1} ''{2}''. diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/infrastructure/BasicInfrastructureService.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/infrastructure/BasicInfrastructureService.sarl new file mode 100644 index 0000000000..f5da86ff3e --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/infrastructure/BasicInfrastructureService.sarl @@ -0,0 +1,39 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.infrastructure + +import io.sarl.sre.services.AbstractSreService +import javax.inject.Singleton + +/** + * This class supports the management of the SRE infrastructure inside a closed environment. + * + *

This service is thread-safe. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +@Singleton +class BasicInfrastructureService extends AbstractSreService implements InfrastructureService { + // +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/infrastructure/InfrastructureService.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/infrastructure/InfrastructureService.sarl new file mode 100644 index 0000000000..e83ee0bbea --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/infrastructure/InfrastructureService.sarl @@ -0,0 +1,43 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.infrastructure + +import com.google.common.util.concurrent.Service + +/** + * This class supports the management of the infrastructure as a service for the SRE platform. + * + *

All the other services must depends on this service. + * + *

The tasks that are done by this service are low-level and must not depend on other services. + * + *

This service is used for released any resource that is shared by several other services. + * For example, Hazelcast instance may be + * release by a specific implementation of this service. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +interface InfrastructureService extends Service { + // +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/AbstractLifecycleService.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/AbstractLifecycleService.sarl new file mode 100644 index 0000000000..d005795aae --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/AbstractLifecycleService.sarl @@ -0,0 +1,505 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.lifecycle + +import io.sarl.lang.core.Agent +import io.sarl.lang.core.DynamicSkillProvider +import io.sarl.sarlspecification.SarlSpecificationChecker +import io.sarl.sre.internal.SmartListenerCollection +import io.sarl.sre.services.AbstractSreService +import io.sarl.sre.services.context.Context +import io.sarl.sre.services.context.ExternalContextMemberListener +import io.sarl.sre.services.executor.ExecutorService +import io.sarl.sre.services.logging.LoggingService +import io.sarl.util.concurrent.Collections3 +import java.text.MessageFormat +import java.util.ArrayList +import java.util.List +import java.util.UUID +import java.util.concurrent.locks.ReadWriteLock +import javax.inject.Provider +import javax.inject.Singleton + +import static io.sarl.sre.services.lifecycle.AgentLife.* +import org.eclipse.xtend.lib.annotations.Accessors + +/** + * Implementation of a spawning service that is based on the other services of the SRE platform. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +@Singleton +abstract class AbstractLifecycleService extends AbstractSreService implements LifecycleService { + + /** Maximum number of agents to be launch by a single thread. + */ + static val CREATION_POOL_SIZE = 128 + + val globalListeners : SmartListenerCollection + + val sarlSpecificationChecker : SarlSpecificationChecker + + val agentCreatorProvider : AgentCreatorProvider + + val skillUninstaller : SkillUninstaller + + val logger : LoggingService + + val platformLifecycleEventEmitter : LifecycleServiceListener + + val platformContextEventEmitter : ExternalContextMemberListener + + val executor : ExecutorService + + var userDynamicSkillProviders : List + + @Accessors(PROTECTED_GETTER) + val lockProvider : Provider + + val userDynamicSkillProvidersLock : ReadWriteLock + + /** + * Constructs the service. + * + * @param sarlSpecificationChecker the tool for checking the validity of the SARL specification supported by + * the agents to launch. + * @param creator the provider of agent creators that is used when spawning agents. + * @param globalListeners is the collection of global listeners to use. + * @param lockProvider the provider of locks. + * @param lifecycleListener the listener at the platform scale on life cycle events. + * @param externalContextListener the listener at the platform scale on context membership events. + * @param skillUninstaller the object this is able to uninstall the skills. + * @param executor the executor service. + * @param logger the logging service. + */ + new ( + sarlSpecificationChecker : SarlSpecificationChecker, + creator : AgentCreatorProvider, + globalListeners : SmartListenerCollection, lockProvider : Provider, + lifecycleListener : Provider, + externalContextListener : Provider, + skillUninstaller : SkillUninstaller, executor : ExecutorService, logger : LoggingService) { + this.sarlSpecificationChecker = sarlSpecificationChecker + this.agentCreatorProvider = creator + this.globalListeners = globalListeners + this.lockProvider = lockProvider + this.userDynamicSkillProvidersLock = lockProvider.get + this.skillUninstaller = skillUninstaller + this.executor = executor + this.logger = logger + this.platformLifecycleEventEmitter = lifecycleListener.get + this.platformContextEventEmitter = externalContextListener.get + } + + /** Replies the backend logging service. + * + * @return the logging service. + */ + protected def getLoggingService : LoggingService { + this.logger + } + + protected def onStart { + this.platformLifecycleEventEmitter.addLifecycleServiceListener + } + + protected def onStop { + this.platformLifecycleEventEmitter.removeLifecycleServiceListener + } + + def addLifecycleServiceListener(agentLifecycleListener : LifecycleServiceListener) { + this.globalListeners.add(typeof(LifecycleServiceListener), agentLifecycleListener) + } + + def removeLifecycleServiceListener(agentLifecycleListener : LifecycleServiceListener) { + this.globalListeners.remove(typeof(LifecycleServiceListener), agentLifecycleListener) + } + + def addKernelAgentLifecycleListener(listener : KernelAgentLifecycleListener) { + this.globalListeners.add(typeof(KernelAgentLifecycleListener), listener) + } + + def removeKernelAgentLifecycleListener(listener : KernelAgentLifecycleListener) { + this.globalListeners.remove(typeof(KernelAgentLifecycleListener), listener) + } + + /** Notify the listeners outside the parent context. + * + * @param agents the destroyed agent. + * @param outerContexts the contexts in which the agent w destroyed. + */ + protected def fireAgentDestroy(^agent : Agent, outerContexts : Iterable) { + // Notify the listeners on the spawn events (not restricted to a single agent) + this.globalListeners.notifyListeners(typeof(LifecycleServiceListener)) [ + agentDestroyed(^agent, outerContexts) + ] + } + + /** Notify the listeners outside the parent context. + * + * @param spawningAgent the spawning agent. + * @param context the context in which the agents were spawned. + * @param agentClazz the type of the spawned agents. + * @param agents the spawned agents. + * @param initializationParameters the initialization parameters. + */ + protected def fireAgentSpawned(spawningAgent : UUID, context : Context, + agentClazz : Class, agents : List, initializationParameters : Object*) { + this.globalListeners.notifyListeners(typeof(LifecycleServiceListener)) [ + agentSpawned(spawningAgent, context, agentClazz, agents, initializationParameters) + ] + } + + /** + * Notifies the listeners about the kernel agent destruction. + */ + protected def fireKernelAgentDestroy { + this.globalListeners.notifyListeners(typeof(KernelAgentLifecycleListener)) [ + kernelAgentDestroyed + ] + } + + def isKillableAgent(^agent : Agent) : boolean { + try { + var innerContext = getLife(^agent, this.lockProvider).innerContext + if (innerContext !== null) { + var participants = innerContext.defaultSpace.getParticipants + if (participants !== null) { + var lck = participants.lock + lck.readLock.lock + try { + if (participants.size > 1 || (participants.size == 1 && !participants.contains(^agent.ID))) { + return false + } + } finally { + lck.readLock.unlock + } + } + } + return true + } catch (exception : Throwable) { + return false + } + } + + def spawnAgent(nbAgents : int, spawningAgent : UUID, parent : Context, agentId : UUID, + agentClazz : Class, params : Object*) : SpawnResult { + spawnAgent(nbAgents, spawningAgent, parent, agentClazz, params) [ + if (agentId !== null && it === 0) { + return agentId + } + return UUID::randomUUID + ] + } + + /** Replies if the service can spawn. + * + * @return {@code true} if the service can spawn an agent. + */ + def canSpawnAgent : boolean { + isRunning + } + + protected def spawnAgent(nbAgents : int, spawningAgent : UUID, parent : Context, agentClazz : Class, params : Object[], + agentIds : (int) => UUID) : SpawnResult { + + val agents = new ArrayList(nbAgents) + val agentsLock = this.lockProvider.get + val errors = new ArrayList() + if (canSpawnAgent && nbAgents > 0) { + // Create the list of the spawned agents during this function execution + try { + // Check if the version of the SARL agent class is compatible. + if (this.sarlSpecificationChecker === null || !this.sarlSpecificationChecker.isValidSarlElement(agentClazz)) { + errors += new InvalidSarlSpecificationException(agentClazz) + } else { + // Create the shared injector that is also able to create the agent instance. + val agentInstanceCreator = this.agentCreatorProvider.getAgentCreator(agentClazz, parent.ID, + nbAgents, dynamicSkillProviders, agentIds) + // Create the block of code for creating a single agent + val agentCreator : Runnable = [ + val ^agent = agentInstanceCreator.get() + if (^agent === null) { + throw new CannotSpawnException(agentClazz) + } + // Start the agent's life + var life = getLife(^agent, this.lockProvider) + try { + var started = false + try { + started = life.start(this.skillUninstaller, getLoggingService, spawningAgent, parent, + params) + } catch (e : Throwable) { + errors += new CannotSpawnException(agentClazz, e) + } + if (started) { + // Add the agent in the system. It is synchronized because additions may occur in parallel + agentsLock.writeLock.lock + try { + agents += ^agent + } finally { + agentsLock.writeLock.unlock + } + ^agent.onAgentCreated + } else if (parent.emptyRootContext) { + // The agent seems to be the boot agent, and it has failed to be launched. + // Moreover the root context is still empty. + // The safer decision should be to stop the kernel. + onAgentFrameworkStop + } + } catch (e : Throwable) { + errors += new CannotSpawnException(agentClazz, e) + } + ] + // Create a single agent with a sequential call; or multiple agents in parallel + if (nbAgents > 1) { + this.executor.executeBlockingTask(nbAgents, CREATION_POOL_SIZE, agentCreator) + } else { + agentCreator.run + } + // Fire the general spawning event + if (!agents.empty) { + fireAgentSpawningEvents(spawningAgent, parent, agentClazz, params, agents) + } + } + } catch (e : CannotSpawnException) { + errors += e + } catch (e : Throwable) { + errors += new CannotSpawnException(agentClazz, e) + } + } else { + errors += new SpawnDisabledException(parent.ID, agentClazz) + } + return new SpawnResult( + (agents.map[it.ID]).unmodifiableView, + errors) + } + + /** This function is called each time the agent framework should be stopped. + * This function is provided for being overridden by the sub-classes of + * the current class. + * + *

Within the {@code AbstractLifecycleService}, this function fires the kernel agent + * destruction event. + * + * @since 0.8.0 + */ + protected def onAgentFrameworkStop { + fireKernelAgentDestroy + } + + /** This function is called each time an agent was created. + * This function is provided for being overridden by the sub-classes of + * the current class. + * + *

Within the {@code AbstractLifecycleService}, this function does nothing. + * + * @param agent the spawned agent. + * @since 0.8.0 + */ + protected def onAgentCreated(^agent : Agent) { + // + } + + /** Fire the events related to the agent spawn. + * + * @param spawningAgent the creator. + * @param parent the context in which the agents are created. + * @param agentClazz the type of the spawned agents. + * @param spawnedAgents the spawnedAgents. + */ + protected def fireAgentSpawningEvents(spawningAgent : UUID, parent : Context, + agentClazz : Class, params : Object[], spawnedAgents : List) { + spawningAgent.fireAgentSpawned(parent, agentClazz, spawnedAgents, params) + val spaceID = parent.defaultSpace.spaceID + for (^agent : spawnedAgents) { + this.platformContextEventEmitter.memberJoined( + parent, spaceID, + ^agent.ID, agentClazz.name) + } + } + + /** Replies if the given context is the root context and is empty. + * + * @param context the agent context to test. + * @return {@code true} if the given context is the root context, and it is empty. + * @Otherwise {@code false}. + */ + protected def isEmptyRootContext(context : ContextReference) { + context.context.isEmptyRootContext + } + + /** Replies if the given context is the root context and is empty. + * + * @param context the agent context to test. + * @return {@code true} if the given context is the root context, and it is empty. + * @Otherwise {@code false}. + */ + protected def isEmptyRootContext(context : Context) { + context !== null && context.rootContext && context.defaultSpace.participants.empty + } + + + def killAgent(^agent : Agent) : boolean { + // We should check if it is possible to kill the agent BEFORE killing it. + if (this.running && ^agent.isKillableAgent()) { + var life = getLife(^agent, this.lockProvider) + var defaultContext = life.defaultContext + var contexts = life.stop(this.skillUninstaller, getLoggingService) + ^agent.onAgentKilled + fireAgentDestructionEvents(^agent, defaultContext, contexts) + return true + } + + return false + } + + /** This function is called each time an agent was killed. + * This function is provided for being overridden by the sub-classes of + * the current class. + * + *

Within the {@code AbstractLifecycleService}, this function does nothing. + * + * @param agent the killed agent. + * @since 0.8.0 + */ + protected def onAgentKilled(^agent : Agent) { + // + } + + protected def fireAgentDestructionEvents(^agent : Agent, defaultContextBeforeKilled : ContextReference, + leavedContexts : Iterable) { + // Leave the default context because the ExternalContextAccessSkill does not fire events related + // to the default space. + this.platformContextEventEmitter.memberLeft( + defaultContextBeforeKilled.context, + defaultContextBeforeKilled.addressInDefaultSpace.spaceID, + ^agent.ID, ^agent.class.name) + // Fire the agent destroy event + ^agent.fireAgentDestroy(leavedContexts) + // Test if the agent is the latest on this kernel. + if (defaultContextBeforeKilled.emptyRootContext) { + onAgentFrameworkStop + } + } + + /** Replies the dynamic skill providers that are defined by the user of the service. */ + protected def getDynamicSkillProviders : Iterable { + this.userDynamicSkillProvidersLock.readLock.lock + try { + if (this.userDynamicSkillProviders === null) { + return emptyList + } + return Collections3::unmodifiableSynchronizedIterable(this.userDynamicSkillProviders, this.userDynamicSkillProvidersLock) + } finally { + this.userDynamicSkillProvidersLock.readLock.unlock + } + } + + /** Add a dynamic skill provider that must be used for the agents that are spawned + * after the call to this function. + * + * @param provider is the provider of skills for newly created agents. + */ + def addDynamicSkillProvider(provider : DynamicSkillProvider) { + assert provider !== null + this.userDynamicSkillProvidersLock.writeLock.lock + try { + if (this.userDynamicSkillProviders === null) { + this.userDynamicSkillProviders = newArrayList + } + this.userDynamicSkillProviders += provider + } finally { + this.userDynamicSkillProvidersLock.writeLock.unlock + } + } + + /** Remove a dynamic skill provider that must be not be used any more for the agents that are spawned. + * + * @param provider is the provider of skills to be removed. + */ + def removeDynamicSkillProvider(provider : DynamicSkillProvider) { + assert provider !== null + var list : List + this.userDynamicSkillProvidersLock.readLock.lock + try { + list = this.userDynamicSkillProviders + } finally { + this.userDynamicSkillProvidersLock.readLock.unlock + } + if (list !== null) { + this.userDynamicSkillProvidersLock.writeLock.lock + try { + list = this.userDynamicSkillProviders + if (list !== null) { + this.userDynamicSkillProviders.remove(provider) + if (this.userDynamicSkillProviders.empty) { + this.userDynamicSkillProviders = null + } + } + } finally { + this.userDynamicSkillProvidersLock.writeLock.unlock + } + } + } + +} + +/** + * This exception is thrown when the agent to spawn is not generated according to a valid SARL specification version. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class InvalidSarlSpecificationException extends RuntimeException { + + new (agentType : Class ) { + super(MessageFormat::format(Messages::InvalidSarlSpecificationException_0, agentType.name)) + } + +} + +/** + * This exception is thrown when the spawning service of agents is disabled. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class SpawnDisabledException extends RuntimeException { + + /** + * @param parentID + * - the identifier of the parent entity that is creating the agent. + * @param agentClazz + * - the type of the agent to spawn. + */ + new (parentID : UUID, agentClazz : Class) { + super(MessageFormat.format(Messages.SpawnDisabledException_0, parentID, agentClazz)); + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/AgentCreatorProvider.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/AgentCreatorProvider.sarl new file mode 100644 index 0000000000..8322feb92d --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/AgentCreatorProvider.sarl @@ -0,0 +1,52 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.lifecycle + +import io.sarl.lang.core.Agent +import io.sarl.lang.core.DynamicSkillProvider +import java.util.UUID +import javax.inject.Provider + +/** + * Provider of agent instance creator. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +interface AgentCreatorProvider { + + /** Build an agent creator based on the given parameters. + * + * @param the type of agent to create. + * @param agentClazz the type of agent to create. + * @param parent the identifier of the agents' parent. + * @param nbAgents the number of agents to create. + * @param skillProviders is a list of additionnal skill providers that should be considered by the created agent. + * @param agentId lambda function which replies the identifier + * of the agent to be created at the given index. + */ + def getAgentCreator(agentClazz : Class, parent : UUID, nbAgents : int, + skillProviders : Iterable, agentId : (int) => UUID) : Provider with T extends Agent + +} + diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/AgentLife.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/AgentLife.sarl new file mode 100644 index 0000000000..e28a2d744e --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/AgentLife.sarl @@ -0,0 +1,826 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.lifecycle + +import io.sarl.core.Destroy +import io.sarl.core.Initialize +import io.sarl.core.Logging +import io.sarl.core.OpenEventSpace +import io.sarl.core.OpenEventSpaceSpecification +import io.sarl.lang.annotation.PrivateAPI +import io.sarl.lang.core.Address +import io.sarl.lang.core.Agent +import io.sarl.lang.core.EventListener +import io.sarl.lang.core.SREutils +import io.sarl.lang.core.Skill +import io.sarl.lang.core.SpaceID +import io.sarl.lang.util.ClearableReference +import io.sarl.lang.util.SynchronizedIterable +import io.sarl.sre.capacities.InternalEventBusCapacity +import io.sarl.sre.services.context.Context +import io.sarl.sre.services.logging.LoggingService +import io.sarl.util.concurrent.Collections3 +import java.lang.ref.WeakReference +import java.util.Collections +import java.util.Map +import java.util.UUID +import java.util.concurrent.locks.ReadWriteLock +import java.util.logging.Level +import javax.inject.Provider +import org.arakhne.afc.util.MultiCollection + +import static extension io.sarl.lang.core.SREutils.* + +/** + * Describes any information that is required by the SRE for supporting the agent's life. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.6.0 + */ +@SuppressWarnings("use_reserved_sarl_annotation") +@PrivateAPI(isCallerOnly = true) +final class AgentLife { + + /** Only one lock for the entire instance in order to minimum memory foot print. + * It may be more efficient at run-time to create more locks, but with bigger memort foot print. + */ + val lock : ReadWriteLock + + var state = AgentState::UNSTARTED + + var agentInstance : Agent + + var innerContextInstance : Context + + var innerSpaceAddress : Address + + var externalContextInstances : Map + + var defaultContextInstance : ContextReference + + var loggingCapacity : ClearableReference + + var eventBusCapacity : ClearableReference + + /** Replies the data structure that is storing the living data of the agent for the SRE. + * If this data structure does not exists, it is automatically created. + * + * @param agent the instance of the agent for which the life description should be retrieved + * @param lockProvider the provider of locks that should be used if the life description must be created. + * @see #getLifeNotCreation + */ + static def getLife(^agent : Agent, lockProvider : Provider) : AgentLife { + // Do not need to synchronize this code because the creation of the agent life + // object is done when spawning the agent, that is synchronously executed. + var life = ^agent.getSreSpecificData(typeof(AgentLife)) + if (life === null) { + life = new AgentLife(lockProvider.get) + life.^agent = ^agent + ^agent.sreSpecificData = life + } + return life + } + + /** Replies the data structure that is storing the living data of the agent for the SRE. + * If this data structure does not exists, it is NOT created. + * + * @param agent the instance of the agent for which the life description should be retrieved + * @since 0.10 + * @see #getLife + */ + @Pure + static def getLifeNoCreation(^agent : Agent) : AgentLife { + // Do not need to synchronize this code because the creation of the agent life + // object is done when spawning the agent, that is synchronously executed. + return ^agent.getSreSpecificData(typeof(AgentLife)) + } + + private new(lock : ReadWriteLock) { + this.lock = lock + } + + /** Replies the lock used by this object. */ + @Pure + def getLock : ReadWriteLock { + this.lock + } + + def toString : String { + this.^agent?.ID + "[" + this.state.name + "]" + } + + /** Replies the agent's logger. + * + * @return the logger. + */ + protected final def getAgentLogger : Logging { + var cap : ClearableReference + var lck = getLock + lck.readLock.lock + try { + cap = this.loggingCapacity + } finally { + lck.readLock.unlock + } + if (cap === null) { + lck.writeLock.lock + try { + cap = this.loggingCapacity + if (cap === null) { + cap = SREutils::getInternalSkillReference(this.^agent, typeof(Logging)) + this.loggingCapacity = cap + } + } finally { + lck.writeLock.unlock + } + } + SREutils::castInternalSkillReference(this.^agent, cap, typeof(Logging)) + } + + /** Replies the agent's event bus. + * + * @return the event bus. + */ + final def getEventBus : InternalEventBusCapacity { + var cap : ClearableReference + var lck = getLock + lck.readLock.lock + try { + cap = this.eventBusCapacity + } finally { + lck.readLock.unlock + } + if (cap === null) { + lck.writeLock.lock + try { + cap = this.eventBusCapacity + if (cap === null) { + cap = SREutils::getInternalSkillReference(this.^agent, typeof(InternalEventBusCapacity)) + this.eventBusCapacity = cap + } + } finally { + lck.writeLock.unlock + } + } + SREutils::castInternalSkillReference(this.^agent, cap, typeof(InternalEventBusCapacity)) + } + + /** Start the agent's life. + * + *

This function is invoked for executing the born stage of the agent. + * + *

If an exception is thrown into the initialization code of the agent, the exception is forwarded. + * + * @param skillUninstaller the skill uninstaller. + * @param logger the logger to use for reporting initialization errors when the agent's logger is unavailable + * @param startError the exception that avoid to start the agent. + * @param spawningAgent the identifier of the agent which has spawn this starting agent. This agent could be outside the spawningContext. + * @param spawningContext the context in which the agent is spawned. + * @param initializationParameters the parameters to give to the agent for its start-up. + * @return {@code true} is successful, i.e. the {@code AgentSpawned} could be fired. + */ + def start(skillUninstaller : SkillUninstaller, logger : LoggingService, + spawningAgent : UUID, spawningContext : Context, initializationParameters : Object*) : boolean { + var st : AgentState + var lck = getLock + lck.readLock.lock + try { + st = this.state + } finally { + lck.readLock.unlock + } + if (st === AgentState::UNSTARTED) { + try { + var continueStart = false + lck.writeLock.lock + try { + st = this.state + if (st === AgentState::UNSTARTED) { + st = AgentState::INITIALIZING + this.state = st + continueStart = true + } + } finally { + lck.writeLock.unlock + } + if (continueStart) { + var eb = spawningContext.attachAgentToPlatform + + // Notify the agent about its creation. + // Assume event handlers were run after returning from the fireEventAndWait function. + var initEvent = new Initialize(spawningAgent, initializationParameters) + initEvent.source = addressInInnerDefaultSpace + // Any exception into the inits are thrown outside the call below. + var delayedEvents = eb.fireEventAndWait(initEvent, true, true) + + lck.readLock.lock + try { + st = this.state + } finally { + lck.readLock.unlock + } + continueStart = false + if (st === AgentState::INITIALIZING) { + lck.writeLock.lock + try { + st = this.state + if (st === AgentState::INITIALIZING) { + st = AgentState::ALIVE + this.state = st + continueStart = true + } + } finally { + lck.writeLock.unlock + } + } + if (continueStart) { + if (delayedEvents !== null) { + for (delayedEvent : delayedEvents) { + eb.fireEvent(delayedEvent) + } + } + return true + } else { + stop(skillUninstaller, logger, true) + return false + } + } else { + stop(skillUninstaller, logger, true) + return false; + } + } catch (e : Throwable) { + try { + this.agentLogger.error(e.localizedMessage, e) + } catch (iex : Throwable) { + logger.kernelLogger.log(Level::SEVERE, e.localizedMessage, e) + } + stop(skillUninstaller, logger, true) + throw e + } + } else { + return false + } + } + + /** Set up the agent in order to be attached properly to the plaform. + * + *

This function creates the agent's internal event bus, and attach the agent to the default space + * of its default context. + * + * @param spawningContext the context in which the agent is spawn. + * @return the internal event bus. + */ + protected def attachAgentToPlatform(spawningContext : Context) : InternalEventBusCapacity { + // Set up the internal bus + val eb = getEventBus + // + // Register the agent on its parent default space. + var defaultSpace = spawningContext.defaultSpace + var addressWithinDefaultSpace = new Address(defaultSpace.spaceID, ^agent.ID) + setDefaultContext(spawningContext, addressWithinDefaultSpace) + defaultSpace.register(eb.associatedEventBusListener) + return eb + } + + /** Detach the agent from the platform. + * + *

This function removes the agent from the default space of its default context. + * + * @param listener the listener on space events that must be unregistered. + */ + protected def detachAgentFromPlatform(listener : EventListener) : void { + var contextReference : ContextReference + var lck = getLock + lck.readLock.lock + try { + contextReference = this.defaultContextInstance + } finally { + lck.readLock.unlock + } + // Unregister the agent on its parent default space. + if (contextReference !== null) { + var ds = contextReference.defaultSpace + if (ds !== null) { + ds.unregister(listener) + } + } + } + + /** Stop the agent's life. + * + *

This function is invoked for executing the killing stage of the agent. + * + * @param skillUninstaller the skill uninstaller. + * @param logger the logger to use for reporting initialization errors when the agent's logger is unavailable + * @param fireDestroy indicates if the {@code Destroy} event should be fired. + * @return the contexts from which the agent was removed. + */ + def stop(skillUninstaller : SkillUninstaller, logger : LoggingService, + fireDestroy : boolean = true) : Iterable { + var lck = getLock + lck.writeLock.lock + try { + this.state = AgentState::DYING + } finally { + lck.writeLock.unlock + } + try { + // Pre-stage for uninstalling the skills (BIC and user defined) + var skills : Iterable + try { + skills = skillUninstaller.uninstallSkillsBeforeDestroy(^agent) + } catch (e : Throwable) { + logger.kernelLogger.log(Level::SEVERE, e.localizedMessage, e) + skills = emptyList + } + + if (fireDestroy) { + try { + // Notify the agent about its destruction. + var ^event = new Destroy + ^event.source = addressInInnerDefaultSpace + this.eventBus.fireEventAndWait(^event, false, false) + } catch (e : Throwable) { + try { + this.agentLogger.error(e.localizedMessage, e) + } catch (ex : Throwable) { + logger.kernelLogger.log(Level::SEVERE, e.localizedMessage, e) + } + } + } + + // Copy the contexts in which the agents is registered in order to reply them. + var enclosingContextsCopy = newArrayList + var contexts = getEnclosingContexts + contexts.lock.readLock.lock + try { + enclosingContextsCopy += contexts + } finally { + contexts.lock.readLock.unlock + } + + // Copy the external event listener in order to unregistered it later. + var listener = this.eventBus.associatedEventBusListener + + // Final stage for uninstalling the skills (BIC and user defined) + try { + skillUninstaller.uninstallSkillsAfterDestroy(^agent, skills) + } catch (e : Throwable) { + logger.kernelLogger.log(Level::SEVERE, e.localizedMessage, e) + } + + // Detach the agent form the platform + listener.detachAgentFromPlatform + + // Force unregistration to the enclosing spaces + return enclosingContextsCopy + } catch (e : Throwable) { + logger.kernelLogger.log(Level::SEVERE, e.localizedMessage, e) + return Collections::emptyList + } finally { + // Clear the references + lck.writeLock.lock + try { + ^agent.setSreSpecificData(null) + this.defaultContextInstance = null + this.externalContextInstances = null + this.agentInstance = null + this.state = AgentState::DEAD + } finally { + lck.writeLock.unlock + } + } + } + + /** Replies the agent associated to this life. + */ + def getAgent : Agent { + var lck = getLock + lck.readLock.lock + try { + return this.agentInstance + } finally { + lck.readLock.unlock + } + } + + /** Change the agent associated to this life. + */ + def setAgent(^agent : Agent) { + var lck = getLock + lck.writeLock.lock + try { + this.agentInstance = ^agent + } finally { + lck.writeLock.unlock + } + } + + /** Replies the agent state. + */ + def getState : AgentState { + var lck = getLock + lck.readLock.lock + try { + return this.state + } finally { + lck.readLock.unlock + } + } + + /** Change the agent state. + */ + def setState(state : AgentState) { + var lck = getLock + assert state !== null + lck.writeLock.lock + try { + this.state = state + } finally { + lck.writeLock.unlock + } + } + + /** Set the inner context reference. + * + * @param innerContext the instance of inner context, or {@code null} + * @return the previous context, or {@code null}. + */ + def setInnerContext(innerContext : Context) : Context { + var old : Context + var lck = getLock + lck.writeLock.lock + try { + old = this.innerContextInstance + this.innerContextInstance = innerContext + } finally { + lck.writeLock.unlock + } + return old + } + + /** Replies the inner context reference. + * + * @return the instance of inner context, or {@code null} + */ + def getInnerContext : Context { + var lck = getLock + lck.readLock.lock + try { + return this.innerContextInstance + } finally { + lck.readLock.unlock + } + } + + /** Replies the address of the agent into the default space of its inner context. + * + * @return the address, never {@code null}. + */ + def getAddressInInnerDefaultSpace : Address { + var adr : Address + var lck = getLock + lck.readLock.lock + try { + adr = this.innerSpaceAddress + } finally { + lck.readLock.unlock + } + if (adr === null) { + lck.writeLock.lock + try { + adr = this.innerSpaceAddress + if (adr === null) { + var aid = this.^agent.ID + var spaceid = new SpaceID(aid, UUID::randomUUID, typeof(OpenEventSpaceSpecification)) + adr = new Address(spaceid, aid); + this.innerSpaceAddress = adr + } + } finally { + lck.writeLock.unlock + } + } + return adr + } + + /** Add a context in which the agent is located. + * + * @param context the context in which the agent is now located. + * @param address the address of the agent in the default space of the given context. + * @return the created reference, or {@code null} if the reference cannot be added (because the given context is already the default context). + */ + def addExternalContext(context : Context, address : Address) : ContextReference { + assert context !== null + assert address !== null + var continueAddition = false + var lck = getLock + lck.readLock.lock + try { + continueAddition = this.defaultContextInstance === null || context.ID != this.defaultContextInstance.context.ID + } finally { + lck.readLock.unlock + } + if (continueAddition) { + var ref = new ContextReference(this, context, address) + lck.writeLock.lock + try { + ensureExternalContextInstances.put(context.ID, ref) + } finally { + lck.writeLock.unlock + } + return ref + } + return null + } + + /** Remove a context in which the agent is located. + */ + def removeExternalContext(context : Context) : ContextReference { + assert context !== null + var ctxs : Map + var lck = getLock + lck.readLock.lock + try { + ctxs = this.externalContextInstances + } finally { + lck.readLock.unlock + } + if (ctxs !== null) { + lck.writeLock.lock + try { + ctxs = this.externalContextInstances + if (ctxs !== null) { + var ctx = ctxs.remove(context.ID) + if (ctxs.empty) { + this.externalContextInstances = null + } + return ctx + } + } finally { + lck.writeLock.unlock + } + } + return null + } + + /** Remove a context in which the agent is located. + */ + def removeExternalContext(context : ContextReference) : ContextReference { + assert context !== null + var ctxs : Map + var lck = getLock + lck.readLock.lock + try { + ctxs = this.externalContextInstances + } finally { + lck.readLock.unlock + } + if (ctxs !== null) { + lck.writeLock.lock + try { + ctxs = this.externalContextInstances + if (ctxs !== null) { + var ctx = ctxs.remove(context.context.ID) + if (ctxs.empty) { + this.externalContextInstances = null + } + return ctx + } + } finally { + lck.writeLock.unlock + } + } + return null + } + + /** Replies the external contexts in which the agent is located. + * The default context is not part of the replied collection. + * + * @see {@link #getEnclosingContexts()} + */ + def getExternalContexts : SynchronizedIterable { + var lck = getLock + lck.readLock.lock + try { + if (this.externalContextInstances === null) { + Collections3::emptySynchronizedSet + } else { + Collections3::unmodifiableSynchronizedIterable(this.externalContextInstances.values, lck) + } + } finally { + lck.readLock.unlock + } + } + + /** Replies the number of external contexts in which the agent is located. + * The default context is not counted. + * + * @return the number of external contexts. + */ + def getExternalContextCount : int { + var lck = getLock + lck.readLock.lock + try { + if (this.externalContextInstances === null) { + return 0 + } + return this.externalContextInstances.size + } finally { + lck.readLock.unlock + } + } + + /** Replies all the contexts in which the agent is located, including the default context and + * the external contexts. + * + * @see {@link #getExternalContexts()} + */ + def getEnclosingContexts : SynchronizedIterable { + var lck = getLock + lck.readLock.lock + try { + if (this.defaultContextInstance !== null) { + var dcs = Collections::singleton(this.defaultContextInstance) + if (this.externalContextInstances === null) { + return Collections3::unmodifiableSynchronizedIterable(dcs, lck) + } else { + var multi = new MultiCollection + multi.addCollection(dcs) + multi.addCollection(this.externalContextInstances.values) + return Collections3::unmodifiableSynchronizedIterable(multi, lck) + } + } else if (this.externalContextInstances === null) { + return Collections3::emptySynchronizedSet + } else { + return Collections3::unmodifiableSynchronizedIterable(this.externalContextInstances.values, lck) + } + } finally { + lck.readLock.unlock + } + } + + + /** Replies the external context with the given identifier. + * + * @param identifer the identifier of the context. + * @return the context, or {@code null} + */ + def getExternalContext(identifier : UUID) : ContextReference { + var lck = getLock + lck.readLock.lock + try { + if (this.externalContextInstances !== null) { + return this.externalContextInstances.get(identifier) + } + } finally { + lck.readLock.unlock + } + return null + } + + /** Change the default context of the agent. + * + * @param context the next context, never {@code null}. + * @param address the address of the agent into the default space of the given context. + * @return the reference to the previous default context. + */ + def setDefaultContext(context : Context, address : Address) : ContextReference { + assert context !== null + assert address !== null + var oldDefaultSpace : ContextReference + var lck = getLock + lck.writeLock.lock + try { + oldDefaultSpace = this.defaultContextInstance + this.defaultContextInstance = new ContextReference(this, context, address) + } finally { + lck.writeLock.unlock + } + removeExternalContext(context) + return oldDefaultSpace + } + + private def ensureExternalContextInstances : Map { + var map : Map + var lck = getLock + lck.readLock.lock + try { + map = this.externalContextInstances + } finally { + lck.readLock.unlock + } + if (map === null) { + lck.writeLock.lock + try { + map = this.externalContextInstances + if (map === null) { + map = newTreeMap(null) + this.externalContextInstances = map + } + } finally { + lck.writeLock.unlock + } + } + return map + } + + /** Replies the default context of the agent. + * + * @return the default context, never {@code null}. + */ + def getDefaultContext : ContextReference { + var lck = getLock + lck.readLock.lock + try { + return this.defaultContextInstance + } finally { + lck.readLock.unlock + } + } + +} + +/** + * Describes any information that is required by the SRE for supporting the agent's life. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.6.0 + */ +final class ContextReference implements Comparable { + + val life : WeakReference + + val contextInstance : WeakReference + + var defaultAddress : Address + + /** Constructor. + * + * @param context the content to reference. + * @param address the address of the agent in the default space of the given context. + */ + new (life : AgentLife, context : Context, address : Address) { + assert life !== null + assert context !== null + assert address !== null + this.life = new WeakReference(life) + this.contextInstance = new WeakReference(context) + this.defaultAddress = address + } + + override toString : String { + var context = this.contextInstance?.get + if(context === null) "[?]" else context.toString + } + + override compareTo(o : ContextReference) : int { + if(o === null) return Integer::MAX_VALUE + return this.contextInstance.get.ID <=> o.contextInstance.get.ID + } + + /** Replies the context. + */ + def getContext : Context { + this.contextInstance.get + } + + /** Replies the default space. + */ + def getDefaultSpace : OpenEventSpace { + this.contextInstance.get.defaultSpace + } + + /** Replies the address of the agent into the default space. + */ + def getAddressInDefaultSpace : Address { + if (this.defaultAddress === null) { + this.defaultAddress = this.contextInstance.get.defaultSpace.getAddress(this.life.get.^agent.ID) + } + this.defaultAddress + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/AgentState.java b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/AgentState.java new file mode 100644 index 0000000000..92026e82d8 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/AgentState.java @@ -0,0 +1,219 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.lifecycle; + +/** + * Describe the states of an agent. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +public enum AgentState { + + /** + * The agent is unstarted: before initialization process. + */ + UNSTARTED { + @Override + public boolean isAsynchronousEventHandling() { + return false; + } + + @Override + public boolean isBlockingEventHandling() { + return false; + } + + @Override + public boolean isPreAlive() { + return true; + } + + @Override + public boolean isAlive() { + return false; + } + + @Override + public boolean isPostAlive() { + return false; + } + }, + + /** + * The agent is under creation. + */ + INITIALIZING { + @Override + public boolean isAsynchronousEventHandling() { + return true; + } + + @Override + public boolean isBlockingEventHandling() { + return true; + } + + @Override + public boolean isPreAlive() { + return false; + } + + @Override + public boolean isAlive() { + return true; + } + + @Override + public boolean isPostAlive() { + return false; + } + }, + + /** + * The owner of the event bus is running. + */ + ALIVE { + @Override + public boolean isAsynchronousEventHandling() { + return true; + } + + @Override + public boolean isBlockingEventHandling() { + return true; + } + + @Override + public boolean isPreAlive() { + return false; + } + + @Override + public boolean isAlive() { + return true; + } + + @Override + public boolean isPostAlive() { + return false; + } + }, + + /** + * The owner of the event bus is under destruction. + */ + DYING { + @Override + public boolean isAsynchronousEventHandling() { + return false; + } + + @Override + public boolean isBlockingEventHandling() { + return true; + } + + @Override + public boolean isPreAlive() { + return false; + } + + @Override + public boolean isAlive() { + return false; + } + + @Override + public boolean isPostAlive() { + return true; + } + }, + + /** + * The owner of the event bus was destroyed. + */ + DEAD { + @Override + public boolean isAsynchronousEventHandling() { + return false; + } + + @Override + public boolean isBlockingEventHandling() { + return false; + } + + @Override + public boolean isPreAlive() { + return false; + } + + @Override + public boolean isAlive() { + return false; + } + + @Override + public boolean isPostAlive() { + return true; + } + }; + + /** Replies if the state accepts asynchronous event handling. + * + * @return {@code true} if the state accept event handling. + * @since 0.5 + */ + public abstract boolean isAsynchronousEventHandling(); + + /** Replies if the state accepts blocking event handling. + * + * @return {@code true} if the state accept block event handling. + * @since 0.5 + */ + public abstract boolean isBlockingEventHandling(); + + /** Replies if the state is one of the living states (initializing and alive). + * + * @return {@code true} if the state is an agent life state. + * @since 0.7 + */ + public abstract boolean isAlive(); + + /** Replies if the state is before the agent life. + * + * @return {@code true} if the state is before the agent life. + * @since 0.7 + */ + public abstract boolean isPreAlive(); + + /** Replies if the state is after the agent life. + * + * @return {@code true} if the state is after the agent life. + * @since 0.5 + */ + public abstract boolean isPostAlive(); + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/AgentTraitLife.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/AgentTraitLife.sarl new file mode 100644 index 0000000000..0eb426e109 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/AgentTraitLife.sarl @@ -0,0 +1,132 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.lifecycle + +import io.sarl.core.AgentTask +import java.lang.ref.WeakReference +import java.util.Comparator +import java.util.Set +import java.util.concurrent.locks.ReadWriteLock + +/** + * Describes any information that is required by SRE for supporting the agent trait's life. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.6.0 + */ +abstract class AgentTraitLife { + + static val COMPARATOR : Comparator> = [ tr1, tr2 | + val t1 = tr1.get + val t2 = tr2.get + if(t1 === t2) return 0 + if(t1 === null) return Integer::MIN_VALUE + if(t2 === null) return Integer::MAX_VALUE + System::identityHashCode(t1) <=> System.identityHashCode(t2) + ] + + /** Only one lock for the entire instance in order to minimum memory foot print. + * It may be more efficient at run-time to create more locks, but with bigger memort foot print. + */ + val lock : ReadWriteLock + + var tasks : Set> + + /** Constructor. + * + * @param lock the synchronization lock to used for this life description. + */ + protected new(lock : ReadWriteLock) { + this.lock = lock + } + + /** Add a reference to a task that is assumed to be cancelled if the behavior becomes unregistered. + * + * @param task the task to save. + */ + def addTaskReference(task : AgentTask) { + this.lock.writeLock.lock + try { + if (this.tasks === null) { + this.tasks = newTreeSet(COMPARATOR) + } + this.tasks += new WeakReference(task) + } finally { + this.lock.writeLock.unlock + } + } + + /** Remove the reference to a task that is assumed to be cancelled if the behavior becomes unregistered. + * + * @param task the task to remove. + */ + def removeTaskReference(task : AgentTask) { + var tsk : Set> + this.lock.readLock.lock + try { + tsk = this.tasks + } finally { + this.lock.readLock.unlock + } + if (tsk !== null) { + this.lock.writeLock.lock + try { + tsk = this.tasks + if (tsk !== null) { + val ref = new WeakReference(task) + tsk -= ref + } + } finally { + this.lock.writeLock.unlock + } + } + } + + /** Remove all the references to the tasks associated to this behavior. + * + * @return the removed tasks. + */ + def removeAllTaskReferences : Set> { + var tsk : Set> = null + this.lock.readLock.lock + try { + tsk = this.tasks + } finally { + this.lock.readLock.unlock + } + if (tsk !== null) { + this.lock.writeLock.lock + try { + tsk = this.tasks + if (tsk !== null) { + this.tasks = null + } + } finally { + this.lock.writeLock.unlock + } + } + return tsk + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/BehaviorLife.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/BehaviorLife.sarl new file mode 100644 index 0000000000..48920bc75c --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/BehaviorLife.sarl @@ -0,0 +1,79 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.lifecycle + +import io.sarl.lang.annotation.PrivateAPI +import io.sarl.lang.core.Behavior +import java.util.concurrent.locks.ReadWriteLock +import javax.inject.Provider + +import static extension io.sarl.lang.core.SREutils.* + +/** + * Describes any information that is required by the SRE for supporting the behavior's life. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.6.0 + */ +@SuppressWarnings("use_reserved_sarl_annotation") +@PrivateAPI(isCallerOnly = true) +final class BehaviorLife extends AgentTraitLife { + + /** Replies the data structure that is storing the living data of the agent for the SRE. + * If this data structure does not exists, it is automatically created. + * + * @param behavior the instance of the behavior for which the life description should be retrieved + * @param lockProvider the provider of locks that should be used if the life description must be created. + * @see #getLifeNoCreation + */ + static def getLife(^behavior : Behavior, lockProvider : Provider) : BehaviorLife { + var life = ^behavior.getSreSpecificData(typeof(BehaviorLife)) + if (life === null) { + life = new BehaviorLife(lockProvider.get) + ^behavior.sreSpecificData = life + } + return life + } + + /** Replies the data structure that is storing the living data of the agent for the SRE. + * If this data structure does not exists, it is NOT created. + * + * @param behavior the instance of the behavior for which the life description should be retrieved + * @since 0.10 + * @see #getLife + */ + @Pure + static def getLifeNoCreation(^behavior : Behavior) : BehaviorLife { + return ^behavior.getSreSpecificData(typeof(BehaviorLife)) + } + + /** Constructor. + * + * @param lock the synchronization lock to used for this life description. + */ + private new(lock : ReadWriteLock) { + super(lock) + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/CannotSpawnException.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/CannotSpawnException.sarl new file mode 100644 index 0000000000..0399d24f83 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/CannotSpawnException.sarl @@ -0,0 +1,52 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.lifecycle + +import io.sarl.lang.core.Agent +import java.text.MessageFormat + +/** + * This exception is thrown when an agent cannot be spawned. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class CannotSpawnException extends RuntimeException { + + /** + * @param agentClazz the type of the agent to spawn. + * @param cause the cause of the exception. + */ + new (agentClazz : Class, cause : Throwable = null) { + super(MessageFormat::format( + Messages::CannotSpawnException_0, + agentClazz, cause?.localizedMessage), + cause) + } + + /** Constructor. + */ + new { + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/InjectionBasedLifecycleService.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/InjectionBasedLifecycleService.sarl new file mode 100644 index 0000000000..47f7385f3f --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/InjectionBasedLifecycleService.sarl @@ -0,0 +1,247 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.lifecycle + +import com.google.inject.AbstractModule +import com.google.inject.Injector +import io.sarl.lang.annotation.PrivateAPI +import io.sarl.lang.core.Agent +import io.sarl.lang.core.DynamicSkillProvider +import io.sarl.lang.core.SREutils +import java.lang.reflect.Constructor +import java.util.UUID +import javax.inject.Provider +import io.sarl.sarlspecification.SarlSpecificationChecker +import io.sarl.sre.internal.SmartListenerCollection +import java.util.concurrent.locks.ReadWriteLock +import io.sarl.sre.services.executor.ExecutorService +import io.sarl.sre.services.context.ExternalContextMemberListener +import io.sarl.sre.services.logging.LoggingService +import javax.inject.Inject +import io.sarl.sre.KernelScope + +/** + * Implementation of a spawning service that is based on the other services of the SRE platform. + * + * @author $Author: srodriguez$ + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class InjectionBasedLifecycleService extends AbstractLifecycleService { + + /** + * Constructs the service with the given (injected) injector. + * + * @param injector + * the injector that should be used by this service for creating the agents. + * @param sarlSpecificationChecker the tool for checking the validity of the SARL specification supported by + * the agents to launch. + * @param skillProvider the provider of skills that should be used to init the agents. + * @param globalListeners is the collection of global listeners to use. + * @param lockProvider the provider of locks. + * @param lifecycleListener the listener at the platform scale on life cycle events. + * @param externalContextListener the listener at the platform scale on context membership events. + * @param skillUninstaller the object this is able to uninstall the skills. + * @param executor the executor service. + * @param logger the logging service. + */ + @Inject + new (injector : Injector, sarlSpecificationChecker : SarlSpecificationChecker, + skillProvider : DynamicSkillProvider, + globalListeners : SmartListenerCollection, + lockProvider : Provider, + @KernelScope lifecycleListener : Provider, + @KernelScope externalContextListener : Provider, + skillUninstaller : SkillUninstaller, + executor : ExecutorService, + logger : LoggingService) { + this(sarlSpecificationChecker, new CreatorFactory(injector, skillProvider), + globalListeners, lockProvider, lifecycleListener, externalContextListener, + skillUninstaller, executor, logger) + } + + /** + * Constructs the service. + * + * @param sarlSpecificationChecker the tool for checking the validity of the SARL specification supported by + * the agents to launch. + * @param creator the provider of agent creators that is used when spawning agents. + * @param globalListeners is the collection of global listeners to use. + * @param lockProvider the provider of locks. + * @param lifecycleListener the listener at the platform scale on life cycle events. + * @param externalContextListener the listener at the platform scale on context membership events. + * @param skillUninstaller the object this is able to uninstall the skills. + * @param executor the executor service. + * @param logger the logging service. + */ + new (sarlSpecificationChecker : SarlSpecificationChecker, creator : AgentCreatorProvider, + globalListeners : SmartListenerCollection, lockProvider : Provider, + lifecycleListener : Provider, + externalContextListener : Provider, + skillUninstaller : SkillUninstaller, executor : ExecutorService, logger : LoggingService) { + super(sarlSpecificationChecker, creator, globalListeners, lockProvider, lifecycleListener, + externalContextListener, skillUninstaller, executor, logger) + } + + /** + * An injection module that is able to inject the parent ID and agent ID when creating an agent. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ + private static class CreatorFactory implements AgentCreatorProvider { + + val injector : Injector + + val skillProvider : DynamicSkillProvider + + new (injector : Injector, skillProvider : DynamicSkillProvider) { + this.injector = injector + this.skillProvider = skillProvider + } + + def getAgentCreator( + agentClazz : Class, + parent : UUID, + nbAgents : int, + skillProviders : Iterable, + agentId : (int) => UUID) : Provider with T extends Agent { + var sproviders = skillProviders.createSkillProvider + val agentInjectionModule = new JustInTimeAgentInjectionModule(agentClazz, parent, agentId, sproviders) + val agentInjector = this.injector.createChildInjector(agentInjectionModule) + return [ + agentInjector.getInstance(agentClazz) + ] + } + + private def createSkillProvider(providers : Iterable) : DynamicSkillProvider { + if (providers !== null) { + var iterator = providers.iterator + if (iterator.hasNext) { + return [^agent, ^capacity | + var r = this.skillProvider.installSkill(^agent, ^capacity) + if (r !== null) { + return r + } + for (provider : providers) { + r = provider.installSkill(^agent, ^capacity) + if (r !== null) { + return r + } + } + return null + ] + } + } + return this.skillProvider + } + + } + + /** + * An injection module that is able to inject the parent ID and agent ID when creating an agent. + * + * @param the type of the generated agent. + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ + @SuppressWarnings("use_reserved_sarl_annotation") + @PrivateAPI(isCallerOnly = true) + private static class JustInTimeAgentInjectionModule extends AbstractModule implements Provider { + + val agentType : Class + + val constructor1 : Constructor + + val constructor2 : Constructor + + val parentID : UUID + + val skillProvider : DynamicSkillProvider + + var identifierComputer : (int) => UUID + + var creationIndex = 0 + + package new (agentType : Class, parentID : UUID, agentID : (int) => UUID, skillProvider : DynamicSkillProvider) { + assert agentType !== null + assert parentID !== null + this.agentType = agentType + this.parentID = parentID + this.identifierComputer = agentID + this.skillProvider = skillProvider + var cons : Constructor = null + var e1 : Exception = null + try { + cons = this.agentType.getConstructor(typeof(UUID), typeof(UUID)) + } catch (exception : Exception) { + cons = null + e1 = exception + } + this.constructor1 = cons + var e2 : Exception = null + try { + cons = this.agentType.getConstructor(typeof(UUID), typeof(UUID), typeof(DynamicSkillProvider)) + } catch (exception : Exception) { + cons = null + e2 = exception +} + this.constructor2 = cons + if (this.constructor1 === null && this.constructor2 === null) { + throw new CannotSpawnException(this.agentType, e1 ?: e2) + } + } + + def configure { + bind(this.agentType).toProvider(this) + } + + def get : T { + var idx : int + synchronized (this) { + idx = this.creationIndex + this.creationIndex ++ + } + var aid = this.identifierComputer.apply(idx) + assert this.constructor1 !== null || this.constructor2 !== null + try { + if (this.constructor2 !== null) { + this.constructor2.accessible = true + return this.constructor2.newInstance(this.parentID, aid, this.skillProvider) + } + this.constructor1.accessible = true + var ^agent = this.constructor1.newInstance(this.parentID, aid) + SREutils::setDynamicSkillProvider(^agent, this.skillProvider) + return ^agent + } catch (exception : Exception) { + throw new CannotSpawnException(this.agentType, exception) + } + } + + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/LifecycleService.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/LifecycleService.sarl new file mode 100644 index 0000000000..c848b92cb9 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/LifecycleService.sarl @@ -0,0 +1,275 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.lifecycle + +import com.google.common.util.concurrent.Service +import io.sarl.lang.core.Agent +import io.sarl.lang.core.DynamicSkillProvider +import io.sarl.sre.services.context.Context +import java.util.EventListener +import java.util.List +import java.util.UUID +import org.eclipse.xtend.lib.annotations.Accessors +import org.eclipse.xtext.xbase.lib.util.ToStringBuilder + +/** + * This service provides the tools to manage the life-cycle of the agents. + * + * @author $Author: srodriguez$ + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +interface LifecycleService extends Service { + + /** + * Spawn agents of the given type, and pass the parameters to its initialization function. + * + *

This function is blocking until all the agents are spawned. + * + * @param nbAgents the number of agents to spawn. + * @param spawningAgent the agent which is spawning. + * @param parent the parent entity that is creating the agents. + * @param agentClazz the type of the agents to spawn. + * @param agentId the identifier of the agent to spawn. If null the identifier is randomly selected. + * If {@code nbAgents} is greater than 1, the agent identifier must be {@code null}. + * @param params the list of the parameters to pass to the agent initialization function. + * @return the result of the spawning action, including the identifiers of the agents, and the failure causes. + */ + def spawnAgent(nbAgents : int, spawningAgent : UUID, parent : Context, agentId : UUID, + agentClazz : Class, params : Object*) : SpawnResult + + /** + * Kill the given agent. + * + *

An agent could be killed only if it does not contain sub-agents. + * + *

Because the agent reference is passed to this function, only the objects which have this reference + * could kill the agent. + * + * @param agent the agent to kill. + * @return {@code true} if the agent was killed by this call; {@code false} if the agent + * is unknown or already killed. + */ + def killAgent(^agent : Agent) : boolean + + /** + * Replies if the given agent could be killed + * + *

An agent could be killed only if it does not contain sub-agents. + * + *

Because the agent reference is passed to this function, only the objects which have this reference + * could kill the agent. + * + * @param agent the agent to kill. + * @return {@code true} if the agent could be killed. + */ + def isKillableAgent(^agent : Agent) : boolean + + /** + * Add a listener on the changes in the current state of an agent. + * + * @param agentLifecycleListener the listener on the any change in the life-cycle of the agent. + */ + def addLifecycleServiceListener(agentLifecycleListener : LifecycleServiceListener) + + /** + * Remove a listener on the changes in the current state of an agent. + * + * @param agentLifecycleListener the listener on the any change in the life-cycle of the agent. + */ + def removeLifecycleServiceListener(agentLifecycleListener : LifecycleServiceListener) + + /** + * Add a listener on the changes related to the kernel agent. + * + * @param listener listener on the spawning events in the local kernel. + */ + def addKernelAgentLifecycleListener(listener : KernelAgentLifecycleListener) + + /** + * Remove a listener on the changes related to the kernel agent. + * + * @param listener listener on the spawning events in the local kernel. + */ + def removeKernelAgentLifecycleListener(listener : KernelAgentLifecycleListener) + + /** Add a dynamic skill provider that must be used for the agents that are spawned + * after the call to this function. + * + * @param provider is the provider of skills for newly created agents. + */ + def addDynamicSkillProvider(provider : DynamicSkillProvider) + + /** Remove a dynamic skill provider that must be not be used any more for the agents that are spawned. + * + * @param provider is the provider of skills to be removed. + */ + def removeDynamicSkillProvider(provider : DynamicSkillProvider) + +} + +/** + * Description of a spawning action's result. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.7.0 + */ +final class SpawnResult { + + @Accessors(PUBLIC_GETTER) + val spawnedAgents : List + + val errors : List + + var cause : Throwable + + new (spawnedAgents : List, errors : List) { + assert spawnedAgents !== null + assert errors !== null + this.spawnedAgents = spawnedAgents + this.errors = errors + } + + final override toString : String { + val builder = new ToStringBuilder(this) + toString(builder) + return builder.toString + } + + /** Build the string representation of this result. + * + * @param buffer the buffer to fill out. + */ + def toString(buffer : ToStringBuilder) { + buffer.addDeclaredFields + } + + /** Replies if the spawning action has encountered a failure. + * + * @return {@code true} if an error was encountered. + */ + def isFailing : boolean { + !this.errors.empty + } + + /** Replies if the spawning action has generated at least one agent. + * + * @return {@code true} if no error was encountered. + */ + def isAgentSpawned : boolean { + !this.spawnedAgents.empty + } + + /** Replies the failing cause. + * + * @return the failing cause. + * @see #throwAnyError() + */ + def getError : Throwable { + if (this.cause === null) { + switch (this.errors.size) { + case 0: + this.cause = null + case 1: + this.cause = this.errors.get(0) + default: { + this.cause = new CannotSpawnException + for (exception : this.errors) { + this.cause.addSuppressed(exception) + } + } + } + } + return this.cause + } + + /** Throw the exception if the result has an error. + * + *

This function does not return when the result contains an error. + * + * @return the failing cause. + * @see #getError() + * @throws Exception the error into the result. + */ + def throwAnyError : void throws Exception { + val cause = getError + if (cause !== null) { + throw cause + } + } + +} + +/** + * Listener on events related to the life-cycle of an agent. + * + * @author $Author: srodriguez$ + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +interface LifecycleServiceListener extends EventListener { + + /** + * Invoked when the agent is spawned. + * + * @param spawningAgent the identifier of the agent which spawns the given agent. + * @param parent the context in which the agent was created. + * @param agentType the type of the spawned agents. + * @param spawnedAgents the spawned agents. + * @param initializationParameters list of parameters that were passed to the agent. + */ + def agentSpawned(spawningAgent : UUID, parent : Context, + agentType : Class, + spawnedAgents : List, + initializationParameters : Object[]) + + /** + * Invoked when the agent is destroyed. + * @param agent the destroyed agent. + * @param outerContexts the contexts from which the given agent were removed after its destruction. + */ + def agentDestroyed(^agent : Agent, outerContexts : Iterable) + +} + +/** + * Listener on events related to the kernel agent. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +interface KernelAgentLifecycleListener extends EventListener { + + /** + * Invoked when the kernel agent is destroyed. + */ + def kernelAgentDestroyed + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/Messages.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/Messages.sarl new file mode 100644 index 0000000000..573983bee6 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/Messages.sarl @@ -0,0 +1,52 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.lifecycle + +import org.eclipse.osgi.util.NLS + +/** Messages. + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +final class Messages extends NLS { + + static val BUNDLE_NAME = typeof(Messages).getPackage.name + ".messages" + + static new { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, typeof(Messages)) + } + + public static var InjectionBasedLifecycleService_0 : String + + public static var CannotSpawnException_0 : String + + public static var InvalidSarlSpecificationException_0 : String + + public static var SpawnDisabledException_0 : String + + private new { + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/SkillUninstaller.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/SkillUninstaller.sarl new file mode 100644 index 0000000000..ecda41dab9 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/SkillUninstaller.sarl @@ -0,0 +1,126 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.lifecycle + +import com.google.common.collect.Collections2 +import com.google.inject.ImplementedBy +import io.sarl.core.Destroy +import io.sarl.lang.annotation.PrivateAPI +import io.sarl.lang.core.Agent +import io.sarl.lang.core.SREutils +import io.sarl.lang.core.Skill +import io.sarl.lang.core.Skill.UninstallationStage +import java.util.ArrayList + +/** + * Install and uninstall the skills. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.6.0 + */ +@ImplementedBy(typeof(BasicSkillUninstaller)) +interface SkillUninstaller { + + /** Uninstall the skills before the destroying event handler of the agent is invoked. + * + * @param agent the agent for which the skills should be uninstalled. + * @return the skills to pass to {@link #uninstallSkillsAfterDestroy(io.sarl.lang.core.Agent, java.util.Iterable)}. + */ + def uninstallSkillsBeforeDestroy(^agent : Agent) : Iterable + + /** Uninstall the skills after the destroying event handler of the agent is invoked. + * + * @param agent the agent for which the skills should be uninstalled. + * @param skills the skills given by {@link #uninstallSkillsBeforeDestroy(io.sarl.lang.core.Agent)}. + */ + def uninstallSkillsAfterDestroy(^agent : Agent, skills : Iterable) + +} + +/** + * Install and uninstall the skills. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.6.0 + */ +@SuppressWarnings("use_reserved_sarl_annotation") +@PrivateAPI(isCallerOnly = true) +class BasicSkillUninstaller implements SkillUninstaller { + + /** Retrieve the skills that are installed into the given agent. + * + *

This function ensures that a skill is not added two times into the replied skills. + * This case may appends when a skill implements multiple capacities. + * + * @return the skills. + */ + private static def getAllSkills(^agent : Agent) : Iterable { + // Get the registered skills + val skills = SREutils.getSkillRepository(^agent) + if (skills !== null) { + return new ArrayList(Collections2.transform(skills.values) [it.get]) + } + return emptyList + } + + def uninstallSkillsBeforeDestroy(^agent : Agent) : Iterable { + val skills = ^agent.getAllSkills + skills.uninstallSkillsPreStage + return skills + } + + def uninstallSkillsAfterDestroy(^agent : Agent, skills : Iterable) { + skills.uninstallSkillsFinalStage + } + + /** Run the uninstallation functions of the skills for the pre stage of the uninstallation process. + * + *

This function is run before the handlers for {@link Destroy} are invoked. + * + * @param skills the skills to uninstall. + * @see #uninstallSkillsFinalStage(Agent) + */ + private static def uninstallSkillsPreStage(skills : Iterable) { + for (^skill : skills) { + SREutils::doSkillUninstallation(^skill, UninstallationStage::PRE_DESTROY_EVENT) + } + } + + /** Run the uninstallation functions of the skills for the final stage of the uninstallation process. + * + *

This function is run after the handlers for {@link Destroy} are invoked. + * + * @param skills the skills to uninstall. + * @see #uninstallSkillsPreStage(Agent) + */ + private static def uninstallSkillsFinalStage(skills : Iterable) { + for (^skill : skills) { + SREutils::doSkillUninstallation(^skill, UninstallationStage::POST_DESTROY_EVENT) + } + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/messages.properties b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/messages.properties new file mode 100644 index 0000000000..e5bc877193 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/lifecycle/messages.properties @@ -0,0 +1,7 @@ +InjectionBasedLifecycleService_0=Unable to invoke the mapCapacity() function for the Agent instance. + +CannotSpawnException_0=Cannot instanciate an agent of type {0} due to: {1} + +InvalidSarlSpecificationException_0=The SARL specification version that was used for generating the type {0} is not compatible with the specification version supported by Janus. + +SpawnDisabledException_0=The spawning of the agents is disabled. The spawning of {1} inside {0} is skipped. diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/logging/JulLoggerCreator.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/logging/JulLoggerCreator.sarl new file mode 100644 index 0000000000..ffd68046ba --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/logging/JulLoggerCreator.sarl @@ -0,0 +1,157 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.logging + +import java.io.PrintStream +import java.util.logging.Formatter +import java.util.logging.Level +import java.util.logging.Logger +import java.util.logging.SimpleFormatter +import javax.inject.Provider + +/** + * Helper for creating a JUL logger. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class JulLoggerCreator { + + val defaultLevel : Level + + val defaultProgramName : String + + val loggerProvider : Provider + + var formatter : Formatter + + /** Build a logger creator. + * + * @param defaultLevel the logging level for new loggers. + * @param defaultProgramName the default name of the program. + * @param loggerProvider the provider of loggers. + * @param formatter the log message formatter to be used. + */ + new (defaultLevel : Level, defaultProgramName : String, + loggerProvider : Provider, formatter : Formatter = null) { + this.defaultLevel = defaultLevel + this.defaultProgramName = defaultProgramName + this.loggerProvider = loggerProvider + setFormatter(formatter) + } + + /** Replies the log formatter. + * + * @return the log formatter, never {@code null}. + */ + synchronized def getFormatter : Formatter { + if (this.formatter === null) { + this.formatter = createFormatter + } + return this.formatter + } + + /** Change the log formatter. + * + * @param formatter the log formatter, or {@code null} to use the default. + */ + synchronized def setFormatter(formatter : Formatter) { + this.formatter = formatter + } + + /** Create a formatter. + * + * @return the newly created instance of formatter. + */ + protected def createFormatter : Formatter { + new SimpleFormatter + } + + /** + * Create a logger with the given output for the console. + * + * @param name the name of the new logger. + * @param output the output. + * @return the logger. + * @since 0.7.0 + */ + def createConsoleLogger(name : String, output : PrintStream) : Logger { + val logger = this.loggerProvider.get + val nhandler = new JulOutputStreamConsoleHandler(output, getFormatter) + var allHandlers = logger.handlers + if (allHandlers !== null) { + for (handler : allHandlers) { + logger.removeHandler(handler) + } + } + logger.addHandler(nhandler) + logger.useParentHandlers = false + logger.level = this.defaultLevel + return logger + } + + /** + * Create a logger for the platform. + * + * @return the logger. + * @since 0.7.0 + */ + def createPlatformLogger() : Logger { + val logger = this.loggerProvider.get + val stderr = new JulStandardErrorOutputConsoleHandler(getFormatter) + stderr.level = Level::ALL + val stdout = new JulStandardOutputConsoleHandler(getFormatter) + stdout.level = Level::ALL + var allHandlers = logger.handlers + if (allHandlers !== null) { + for (handler : allHandlers) { + logger.removeHandler(handler) + } + } + logger.addHandler(stderr) + logger.addHandler(stdout) + logger.useParentHandlers = false + logger.level = Level::ALL + return logger + } + + /** + * Create a logger with the given name for a platform module (kernel or agent). + * + * @param name the name of the new logger. If {@code null}, the default program name is used. + * @param parent the parent logger. + * @return the logger. + * @since 0.7.0 + */ + def createModuleLogger(name : String = null, parent : Logger) : Logger { + val thename = if (name.isNullOrEmpty) this.defaultProgramName else name + val logger = Logger::getLogger(thename) + if (parent !== null) { + logger.parent = parent + } + logger.useParentHandlers = true + logger.level = this.defaultLevel + return logger + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/logging/JulLoggingService.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/logging/JulLoggingService.sarl new file mode 100644 index 0000000000..8b7ef3f7d5 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/logging/JulLoggingService.sarl @@ -0,0 +1,94 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.logging + +import io.sarl.sre.services.AbstractSreService +import java.util.logging.Level +import java.util.logging.Logger +import javax.inject.Inject +import javax.inject.Singleton + +/** + * This class enables to log information by ensuring that the values of the parameters are not evaluated until the information + * should be really log, according to the log level. The logger is injected. + * + *

The LoggingService considers the parameters of the functions as:

    + *
  • the message is the the message in the property file;
  • + *
  • the parameters are the values that will replace the strings {0}, {1}, {2}... in the text extracted from the + * resource property.
  • + *
+ * + *

If a Throwable is passed as parameter, the text of the exception is retrieved. + * + *

If a Callable is passed as parameter, the object is automatically called. + * + *

If a LogParam is passed as parameter, the toString function will be invoked. + * + *

For all the other objects, the {@link #toString()} function is invoked. + * + *

This service is thread-safe. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +@Singleton +class JulLoggingService extends AbstractSreService implements LoggingService { + + var platformLoggerInstance : Logger + + var kernelLoggerInstance : Logger + + val loggerCreator : JulLoggerCreator + + /** Constructor. + * + * @param loggerCreator the creator. + */ + @Inject + new (loggerCreator : JulLoggerCreator) { + this.loggerCreator = loggerCreator + } + + override getPlatformLogger : Logger { + if (this.platformLoggerInstance === null) { + this.platformLoggerInstance = this.loggerCreator.createPlatformLogger() + } + this.platformLoggerInstance + } + + override getKernelLogger : Logger { + if (this.kernelLoggerInstance === null) { + this.kernelLoggerInstance = this.loggerCreator.createModuleLogger(getPlatformLogger) + } + this.kernelLoggerInstance + } + + def createAgentLogger(name : String, initialLevel : Level = null) : Logger { + val logger = this.loggerCreator.createModuleLogger(name, getPlatformLogger) + if (initialLevel !== null) { + logger.level = initialLevel + } + return logger + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/logging/JulStreams.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/logging/JulStreams.sarl new file mode 100644 index 0000000000..3b2e1caf83 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/logging/JulStreams.sarl @@ -0,0 +1,142 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.logging + +import java.io.PrintStream +import java.util.logging.Formatter +import java.util.logging.Level +import java.util.logging.LogRecord +import java.util.logging.SimpleFormatter +import java.util.logging.StreamHandler +import org.arakhne.afc.inputoutput.stream.WriterOutputStream + +/** A console handler that supports to be link to the standard output or the standard error output. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.6.0 + */ +class JulOutputStreamConsoleHandler extends StreamHandler { + + /** + * Constructor. + * + * @param stream the original stream. + */ + new (stream : PrintStream, formatter : Formatter = null) { + super(stream.ensureOut, formatter.ensureFormatter) + assert stream !== null, "a logger handler cannot have a null output stream" + this.level = Level::ALL + } + + @SuppressWarnings("discouraged_reference") + private static def ensureOut(out : PrintStream) : PrintStream { + if (out === null) { + new PrintStream(new WriterOutputStream(System.console.writer)) + } else { + out + } + } + + @SuppressWarnings("discouraged_reference") + private static def ensureFormatter(formatter : Formatter) : Formatter { + if (formatter === null) { + new SimpleFormatter + } else { + formatter + } + } + + def publish(record : LogRecord) { + super.publish(record) + flush + } + + /** Replies if the given log level is loggable. + * + * @param recordLevel the level to test. + * @return {@code true} if loggable level. + */ + protected def isLoggableRecord(recordLevel : Level) : boolean { + true + } + + override isLoggable(record : LogRecord) : boolean { + if (record !== null) { + val level = record.level + assert level !== null + if (level.isLoggableRecord) { + return super.isLoggable(record) + } + } + return false + } + +} + +/** A console handler that supports to be link to the standard output. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.6.0 + */ +class JulStandardOutputConsoleHandler extends JulOutputStreamConsoleHandler { + + /** Constructor. + */ + @SuppressWarnings("discouraged_reference") + new(formatter : Formatter = null) { + super(System::out, formatter) + } + + def isLoggableRecord(level : Level) : boolean { + level !== Level.WARNING && level !== Level.SEVERE + } + +} + +/** A console handler that supports to be link to the standard error output. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.6.0 + */ +class JulStandardErrorOutputConsoleHandler extends JulOutputStreamConsoleHandler { + + /** Constructor. + */ + @SuppressWarnings("discouraged_reference") + new(formatter : Formatter = null) { + super(System::err, formatter) + this.level = Level::WARNING + } + + def isLoggableRecord(level : Level) : boolean { + level === Level.WARNING || level === Level.SEVERE + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/logging/LoggingService.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/logging/LoggingService.sarl new file mode 100644 index 0000000000..0fa9d0062d --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/logging/LoggingService.sarl @@ -0,0 +1,67 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.logging + +import com.google.common.util.concurrent.Service +import java.util.logging.Level +import java.util.logging.Logger + +/** + * This class enables to log information by ensuring that the values of the parameters are not evaluated until the information + * should be really log, according to the log level. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +interface LoggingService extends Service { + + /** + * Create a logger for an agent. + * + * @param name the name of the logger, usually the name of the agent. + * @param initialLevel the initial level to associate to the logger. + * @return the logger. + * @since 0.7.0 + */ + def createAgentLogger(name : String, initialLevel : Level = null) : Logger + + /** + * Replies the logger used by the platform. The platform logger is the root logger + * for all the other loggers (for kernel and agents). + * + * @param name the name of the sub-logger. + * @return the logger. + * @since 0.7.0 + */ + def getPlatformLogger : Logger + + /** + * Replies the logger used by the kernel. + * + * @param name the name of the sub-logger. + * @return the logger. + * @since 0.7.0 + */ + def getKernelLogger : Logger + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/logging/QuietLoggingService.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/logging/QuietLoggingService.sarl new file mode 100644 index 0000000000..762a6deae3 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/logging/QuietLoggingService.sarl @@ -0,0 +1,94 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.logging + +import io.sarl.sre.services.AbstractSreService +import java.util.logging.Handler +import java.util.logging.Level +import java.util.logging.LogRecord +import java.util.logging.Logger +import javax.inject.Singleton + +/** + * This class provides an implementation of the {@link LoggingService} that outputs nothing. + * + *

This service is thread-safe. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +@Singleton +class QuietLoggingService extends AbstractSreService implements LoggingService { + + var platformLoggerInstance : Logger + + var kernelLoggerInstance : Logger + + override getPlatformLogger : Logger { + if (this.platformLoggerInstance === null) { + this.platformLoggerInstance = new QuietLogger + } + this.platformLoggerInstance + } + + override getKernelLogger : Logger { + if (this.kernelLoggerInstance === null) { + this.kernelLoggerInstance = new QuietLogger + } + this.kernelLoggerInstance + } + + def createAgentLogger(name : String, initialLevel : Level = null) : Logger { + return new QuietLogger + } + + /** Quiet logger. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ + private static class QuietLogger extends Logger { + + new { + super ("", null) + this.level = Level::OFF + } + + override log(record : LogRecord) { + // Do nothing + } + + override addHandler(h : Handler) { + // Do nothing + } + + override removeHandler(h : Handler) { + // Do nothing + } + + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/messages.properties b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/messages.properties new file mode 100644 index 0000000000..b36efc5474 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/messages.properties @@ -0,0 +1,12 @@ +StartingPhaseAccessors_0=Starting service {0} asynchronously. +StartingPhaseAccessors_1=Starting service {0} synchronously. +StartingPhaseAccessors_2={0} started. +StoppingPhaseAccessors_0=Stopping service {0} asynchronously. +StoppingPhaseAccessors_1=Stopping service {0} synchronously. +StoppingPhaseAccessors_2={0} stopped. +AbstractServiceManager_0=Waiting for services termination. +AbstractServiceManager_1=Waiting for services starting. +AbstractServiceManager_2=Detected services:\n* Infrastructure: {0}\n* Dependent services: {1}\n* Free services: {2} +AbstractServiceManager_3=Stopping all services. +AbstractServiceManager_4=Releasing of resources before shuting down. +GoogleServiceManager_0=Failure in service {0}. diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/AbstractNamespaceFinder.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/AbstractNamespaceFinder.sarl new file mode 100644 index 0000000000..e2b910144f --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/AbstractNamespaceFinder.sarl @@ -0,0 +1,99 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.namespace + +import io.sarl.lang.annotation.PrivateAPI +import io.sarl.lang.core.Agent +import io.sarl.lang.core.Space +import io.sarl.sre.capacities.InformedEventListener +import io.sarl.sre.naming.SarlName +import io.sarl.sre.services.context.Context +import io.sarl.sre.services.context.ContextService +import io.sarl.sre.spaces.SpaceWithParticipants +import java.util.UUID +import org.eclipse.xtend.lib.annotations.Accessors + +/** + * Abstract implementation of a finder into the namespaces. + * + * @param the type of name that is supported by this finder. + * @param the type of object that is searching for. + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +abstract class AbstractNamespaceFinder implements INamespaceFinder { + + @Accessors(PROTECTED_GETTER) + val contextService : ContextService + + /** Constructor. + * + * @param service the service that is managing the contexts and the spaces. + */ + new (service : ContextService) { + this.contextService = service + } + + /** Find the agent. + */ + @SuppressWarnings("use_reserved_sarl_annotation") + @PrivateAPI(isCallerOnly = true) + protected def findAgent(contextId : UUID, spaceId : UUID, agentId : UUID) : Agent { + if (agentId !== null) { + var cid = contextId + var ctx : Context + if (cid === null) { + ctx = this.contextService.rootContext + } else { + ctx = this.contextService.getContext(cid) + if (ctx === null) { + return null + } + } + assert ctx !== null + var sid = spaceId + var ^space : Space + if (sid === null) { + ^space = ctx.defaultSpace + } else { + ^space = ctx.getSpace(sid) + if (^space === null) { + return null + } + } + if (^space instanceof SpaceWithParticipants) { + var participantMap = ^space.internalParticipantStructure + var participant = participantMap.get(agentId) + if (participant !== null) { + var listener = participant.participant + if (listener instanceof InformedEventListener) { + return listener.ownerInstance + } + } + } + } + return null + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/AbstractNamespaceService.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/AbstractNamespaceService.sarl new file mode 100644 index 0000000000..91415f15eb --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/AbstractNamespaceService.sarl @@ -0,0 +1,94 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.namespace + +import io.sarl.sre.naming.SarlName +import io.sarl.sre.services.AbstractSreService + +/** + * Abstract implementation of a service that manages name spaces into the SRE. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +abstract class AbstractNamespaceService extends AbstractSreService implements NamespaceService { + + final override findObject(name : SarlName, type : Class) : T with T { + assert type !== null + var obj = findObject(name) + if (obj !== null && type.isInstance(obj)) { + return type.cast(obj) + } + return null + } + + final override findObject(name : SarlName) : Object { + if (name === null) { + return null + } + var associatedObject = name.associatedObject + if (associatedObject === null) { + var obj = findObjectWithoutFragment(name) + if (name.hasFragment) { + // Treat the fragment + var field = obj.getDeclaredField(name) + if (field !== null) { + associatedObject = field + } + return field + } else { + try { + associatedObject = obj + } catch (ex : ClassCastException) { + // + } + } + } + return associatedObject + } + + private static def getDeclaredField(obj : Object, name : SarlName) : FieldAccess { + var type = obj.class + while (type !== null && typeof(Object) != type) { + try { + var field = type.getDeclaredField(name.fragment) + if (field !== null) { + return new FieldAccess(name, field, obj) + } + } catch (ex : Throwable) { + // + } + type = type.superclass + } + return null + } + + /** Find an object with the given name, but ignoring the fragment. + * + * @param name the name of the object, never {@code null}. + * @return the object, or {@code null} if the object was not found. + */ + protected abstract def findObjectWithoutFragment(name : SarlName) : Object + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/AgentNamespaceFinder.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/AgentNamespaceFinder.sarl new file mode 100644 index 0000000000..e35a5af358 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/AgentNamespaceFinder.sarl @@ -0,0 +1,62 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.namespace + +import io.sarl.lang.core.Agent +import io.sarl.sre.naming.AgentName +import io.sarl.sre.naming.NameScheme +import io.sarl.sre.services.context.ContextService +import javax.inject.Inject +import javax.inject.Singleton + +/** + * Implementation of a finder of agent into the namespaces. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +@Singleton +class AgentNamespaceFinder extends AbstractNamespaceFinder { + + /** Constructor. + * + * @param service the service that is managing the contexts and the spaces. + */ + @Inject + new (service : ContextService) { + super(service) + } + + override getScheme : NameScheme { + NameScheme::AGENT + } + + override find(name : AgentName) : Agent { + if (name === null) { + return null + } + return findAgent(name.contextId, name.spaceId, name.agentId) + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/BehaviorNamespaceFinder.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/BehaviorNamespaceFinder.sarl new file mode 100644 index 0000000000..1afb37bf9a --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/BehaviorNamespaceFinder.sarl @@ -0,0 +1,83 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.namespace + +import io.sarl.core.Behaviors +import io.sarl.lang.annotation.PrivateAPI +import io.sarl.lang.core.Behavior +import io.sarl.lang.core.SREutils +import io.sarl.sre.naming.BehaviorName +import io.sarl.sre.naming.NameScheme +import io.sarl.sre.services.context.ContextService +import javax.inject.Inject +import javax.inject.Singleton + +/** + * Implementation of a finder of behavior into the namespaces. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +@Singleton +class BehaviorNamespaceFinder extends AbstractNamespaceFinder { + + /** Constructor. + * + * @param service the service that is managing the contexts and the spaces. + */ + @Inject + new (service : ContextService) { + super(service) + } + + override getScheme : NameScheme { + NameScheme::BEHAVIOR + } + + @SuppressWarnings("use_reserved_sarl_annotation") + @PrivateAPI(isCallerOnly = true) + override find(name : BehaviorName) : Behavior { + if (name === null) { + return null + } + var behaviorType = name.behaviorType + if (behaviorType !== null) { + var ^agent = findAgent(name.contextId, name.spaceId, name.agentId) + if (^agent !== null) { + var behaviors = SREutils::getInternalSkill(^agent, typeof(Behaviors)) + var index = 0 + for (beh : behaviors.registeredBehaviors) { + if (behaviorType.isInstance(beh)) { + if (name.behaviorIndex < 0 || name.behaviorIndex === index) { + return beh + } + index++ + } + } + } + } + return null + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/ContextNamespaceFinder.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/ContextNamespaceFinder.sarl new file mode 100644 index 0000000000..ab15904e38 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/ContextNamespaceFinder.sarl @@ -0,0 +1,67 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.namespace + +import io.sarl.sre.naming.ContextName +import io.sarl.sre.naming.NameScheme +import io.sarl.sre.services.context.Context +import io.sarl.sre.services.context.ContextService +import javax.inject.Inject +import javax.inject.Singleton + +/** + * Implementation of a finder of context into the namespaces. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +@Singleton +class ContextNamespaceFinder extends AbstractNamespaceFinder { + + /** Constructor. + * + * @param service the service that is managing the contexts and the spaces. + */ + @Inject + new (service : ContextService) { + super(service) + } + + + override getScheme : NameScheme { + NameScheme::CONTEXT + } + + override find(name : ContextName) : Context { + if (name === null) { + return null + } + var id = name.contextId + if (id !== null) { + return this.contextService.getContext(id) + } + return this.contextService.rootContext + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/FieldAccess.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/FieldAccess.sarl new file mode 100644 index 0000000000..aff41c004a --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/FieldAccess.sarl @@ -0,0 +1,100 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.namespace + +import io.sarl.sre.naming.SarlName +import java.lang.reflect.Field +import java.lang.reflect.Modifier +import org.eclipse.xtend.lib.annotations.Accessors + +/** + * Accessor to a field. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class FieldAccess { + + @Accessors(PUBLIC_GETTER) + val name : SarlName + + @Accessors(PUBLIC_GETTER) + val field : Field + + @Accessors(PUBLIC_GETTER) + val instance : Object + + /** Constructor. + * + * @param name the name of the field. + * @param field the field declaration. + * @param instance the object to have access to. + */ + new (name : SarlName, field : Field, instance : Object) { + assert !Modifier::isStatic(field.modifiers) + this.name = name + this.field = field + this.instance = instance + this.field.accessible = true + } + + /** Replies the value of the field. + * + * @return the field's value. + */ + @Pure + def get : Object { + try { + return this.field.get(this.instance) + } catch (ex : Throwable) { + // + } + return null + } + + /** Change the value of the field. + * + * @param value the field's value. + * @return the value before setting. + * @throws IllegalArgumentException if the given value cannot be assigned to the field. + */ + def set(value : Object) : Object throws IllegalArgumentException { + try { + val oldValue = this.field.get(this.instance) + this.field.set(this.instance, value) + return oldValue + } catch (ex : IllegalArgumentException) { + throw ex + } catch (ex : Throwable) { + // + } + return null + } + + @Pure + override toString : String { + this.field.name + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/FinderBasedNamespaceService.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/FinderBasedNamespaceService.sarl new file mode 100644 index 0000000000..68a5786010 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/FinderBasedNamespaceService.sarl @@ -0,0 +1,84 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.namespace + +import io.sarl.sre.boot.internal.services.NamespaceFinders +import io.sarl.sre.naming.NameScheme +import io.sarl.sre.naming.SarlName +import java.util.Set +import javax.inject.Inject + +/** + * Implementation of a namespace service that uses the namespace finders. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class FinderBasedNamespaceService extends AbstractNamespaceService { + + @SuppressWarnings("raw_type") + val finders = newTreeMap(null) + + /** Change the set of namespace finders that is used by this service. + * + *

This function is annoted in order to be used by the Guice injector. + * + * @param finders the set of finders. + */ + @SuppressWarnings("raw_type") + @Inject + def setNamespaceFinders(@NamespaceFinders finders : Set) { + if (finders !== null) { + this.finders.clear + for (finder : finders) { + finder.addNamespaceFinder + } + } + } + + /** Add a namespace finder. + * + * @param finder the namespace finder to add. + */ + def addNamespaceFinder(finder : INamespaceFinder) : void { + this.finders.put(finder.scheme, finder) + } + + /** Remove a namespace finder. + * + * @param scheme the scheme of the finder to remove. + */ + def removeNamespaceFinder(scheme : NameScheme) : void { + this.finders.remove(scheme) + } + + def findObjectWithoutFragment(name : SarlName) : Object { + val finder = this.finders.get(name.scheme) + if (finder !== null) { + return finder.find(name) + } + return null + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/INamespaceFinder.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/INamespaceFinder.sarl new file mode 100644 index 0000000000..cb83649fb7 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/INamespaceFinder.sarl @@ -0,0 +1,51 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.namespace + +import io.sarl.sre.naming.NameScheme +import io.sarl.sre.naming.SarlName + +/** + * An object that is able to find a specific type of obtain from a name. + * + * @param the type of name that is supported by this finder. + * @param the type of object that is searching for. + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +interface INamespaceFinder { + + /** Replies the name scheme supported by this finder. */ + @Pure + def getScheme : NameScheme + + /** Find and replies the object with the given name. + * + * @param name the name of the object to search for. + * @return the object, or {@code null} if the object is not found. + */ + @Pure + def find(name : N) : O + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/NamespaceService.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/NamespaceService.sarl new file mode 100644 index 0000000000..14c2778be4 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/NamespaceService.sarl @@ -0,0 +1,59 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.namespace + +import com.google.common.util.concurrent.Service +import io.sarl.sre.naming.SarlName + +/** + * This service enables to manage the name spaces into the SRE. + * + *

Each object within the SRE may be identified by a name, stored into an {@link SarlName}. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +interface NamespaceService extends Service { + + /** + * Finds and replies the object with the given name and of the given type. + * + * @param name the name of the object. See the documentation of {@link NamespaceService} + * for details. + * @return the root context. A {@code null} value is replied if the object is not found. + */ + @Pure + def findObject(name : SarlName, type : Class) : T with T + + /** + * Finds and replies the object with the given name and of the given type. + * + * @param name the name of the object. See the documentation of {@link NamespaceService} + * for details. + * @return the object with the given name. A {@code null} value is replied if the object is not found. + */ + @Pure + def findObject(name : SarlName) : Object + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/ServiceNamespaceFinder.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/ServiceNamespaceFinder.sarl new file mode 100644 index 0000000000..9af1e3a026 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/ServiceNamespaceFinder.sarl @@ -0,0 +1,72 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.namespace + +import com.google.common.util.concurrent.Service +import com.google.inject.Injector +import io.sarl.sre.naming.NameScheme +import io.sarl.sre.naming.ServiceName +import org.eclipse.xtend.lib.annotations.Accessors +import javax.inject.Inject + +/** + * Implementation of a finder of service into the namespaces. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class ServiceNamespaceFinder implements INamespaceFinder { + + @Accessors(PROTECTED_GETTER) + val injector : Injector + + /** Constructor. + * + * @param injector the injector to be used by this finder for obtaining the service instances. + */ + @Inject + new (injector : Injector) { + this.injector = injector + } + + override getScheme : NameScheme { + NameScheme::SERVICE + } + + override find(name : ServiceName) : Service { + if (name === null) { + return null + } + var id = name.serviceType + if (id !== null) { + try { + return this.injector.getInstance(id) + } catch (ex : Throwable) { + // + } + } + return null + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/SkillNamespaceFinder.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/SkillNamespaceFinder.sarl new file mode 100644 index 0000000000..a64df76f41 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/SkillNamespaceFinder.sarl @@ -0,0 +1,81 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.namespace + +import io.sarl.lang.annotation.PrivateAPI +import io.sarl.lang.core.SREutils +import io.sarl.lang.core.Skill +import io.sarl.lang.core.UnimplementedCapacityException +import io.sarl.sre.naming.NameScheme +import io.sarl.sre.naming.SkillName +import io.sarl.sre.services.context.ContextService +import javax.inject.Inject +import javax.inject.Singleton + +/** + * Implementation of a finder of skill into the namespaces. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +@Singleton +class SkillNamespaceFinder extends AbstractNamespaceFinder { + + /** Constructor. + * + * @param service the service that is managing the contexts and the spaces. + */ + @Inject + new (service : ContextService) { + super(service) + } + + override getScheme : NameScheme { + NameScheme::SKILL + } + + @SuppressWarnings("use_reserved_sarl_annotation") + @PrivateAPI(isCallerOnly = true) + override find(name : SkillName) : Skill { + if (name === null) { + return null + } + var ^capacity = name.^capacity + if (^capacity !== null) { + var ^agent = findAgent(name.contextId, name.spaceId, name.agentId) + if (^agent !== null) { + try { + var ^skill = SREutils::getInternalSkill(^agent, ^capacity) + if (^skill instanceof Skill) { + return ^skill + } + } catch (ex : UnimplementedCapacityException) { + // + } + } + } + return null + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/SpaceNamespaceFinder.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/SpaceNamespaceFinder.sarl new file mode 100644 index 0000000000..77c51973e2 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/namespace/SpaceNamespaceFinder.sarl @@ -0,0 +1,81 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.namespace + +import io.sarl.lang.core.Space +import io.sarl.sre.naming.NameScheme +import io.sarl.sre.naming.SpaceName +import io.sarl.sre.services.context.Context +import io.sarl.sre.services.context.ContextService +import javax.inject.Inject +import javax.inject.Singleton + +/** + * Implementation of a finder of space into the namespaces. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +@Singleton +class SpaceNamespaceFinder extends AbstractNamespaceFinder { + + /** Constructor. + * + * @param service the service that is managing the contexts and the spaces. + */ + @Inject + new (service : ContextService) { + super(service) + } + + override getScheme : NameScheme { + NameScheme::SPACE + } + + override find(name : SpaceName) : Space { + if (name === null) { + return null + } + var ctx : Context = null + var contextId = name.contextId + if (contextId !== null) { + ctx = this.contextService.getContext(contextId) + if (ctx === null) { + return null + } + } + if (ctx === null) { + ctx = this.contextService.rootContext + } + if (ctx !== null) { + var spaceId = name.spaceId + if (spaceId !== null) { + return ctx.getSpace(spaceId) + } + return ctx.defaultSpace + } + return null + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/AbstractFieldProbe.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/AbstractFieldProbe.sarl new file mode 100644 index 0000000000..f5636d1df9 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/AbstractFieldProbe.sarl @@ -0,0 +1,162 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.probing + +import com.google.common.util.concurrent.Service +import io.sarl.lang.core.Agent +import io.sarl.lang.core.AgentTrait +import io.sarl.lang.core.Behavior +import io.sarl.sre.internal.SmartListenerCollection +import io.sarl.sre.naming.NameScheme +import io.sarl.sre.services.lifecycle.AgentLife +import io.sarl.sre.services.lifecycle.BehaviorLife +import io.sarl.sre.services.namespace.FieldAccess +import java.net.URI +import java.util.concurrent.locks.ReadWriteLock +import javax.inject.Provider +import org.eclipse.xtend.lib.annotations.Accessors + +/** + * Abstract probe implementation is linked to a field. A probe is thread-safe. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +abstract class AbstractFieldProbe extends AbstractProbe { + + @Accessors(PROTECTED_GETTER) + val field : FieldAccess + + var activation : (Object) => boolean + + /** Constructor. + * + * @param manager the creator of this probe, and its manager. + * @param name the name of the probe + * @param uri the UI of the field. + * @param type the expected type of the probed value. + * @param field the accessor to the probed field. + * @param listenerCollectionProvider the provider of listener collections. + * @param lockProvider the provider of synchronization locks. + */ + protected new ( + manager : IProbeReleaseListener, + name : String, + uri : URI, + type : Class, + field : FieldAccess, + listenerCollectionProvider : Provider>, + lockProvider : Provider) { + super(manager, name, uri, type, listenerCollectionProvider, lockProvider) + assert field !== null + this.field = field + } + + /** Read the value from the associated field. + * This function is not thread-safe by itself. + * + * @return the value of the field, or {@code null} if the value is evaluated to + * {@code null} or the type of the value is not compatible with the one + * replied by {@link #getType()}. + */ + protected def readField : T { + var value = this.field.get + if (value === null) { + return null + } + try { + return getType.cast(value) + } catch (ex : Throwable) { + return null + } + } + + /** Write the value to the associated field. + * + * @param value is the new value to write into the field. + * @return the value of the field before its change, or {@code null} if the old value is evaluated to + * {@code null} or the type of the old value is not compatible with the one + * replied by {@link #getType()}. + */ + protected def writeField(value : T) : T { + try { + var old = this.field.set(value) + if (old === null) { + return null + } + return getType.cast(value) + } catch (ex : Throwable) { + return null + } + } + + private static def createActivation(scheme : NameScheme) : (Object)=>boolean { + switch (scheme) { + case AGENT: { + return [ + var life = AgentLife::getLifeNoCreation(it as Agent) + if (life !== null) { + return life.state.alive + } + return false + ] + } + case BEHAVIOR: { + return [ + return BehaviorLife::getLifeNoCreation(it as Behavior) !== null + ] + } + case SKILL, + case CONTEXT: { + return [ + var ag = (it as AgentTrait).owner + if (ag !== null) { + var life = AgentLife::getLifeNoCreation(ag) + if (life !== null) { + return life.state.alive + } + } + return false + ] + } + case SPACE: { + return [true] + } + case SERVICE: { + return [(it as Service).isRunning] + } + } + throw new IllegalArgumentException + } + + /** Replies if the probed object is active. + */ + protected def isActiveObject : boolean { + if (this.activation === null) { + this.activation = this.field.name.scheme.createActivation + } + return this.activation.apply(this.field.instance) + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/AbstractProbe.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/AbstractProbe.sarl new file mode 100644 index 0000000000..689e4292e6 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/AbstractProbe.sarl @@ -0,0 +1,261 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.probing + +import io.sarl.sre.internal.SmartListenerCollection +import java.lang.ref.WeakReference +import java.net.URI +import java.util.concurrent.locks.ReadWriteLock +import javax.inject.Provider +import org.eclipse.xtend.lib.annotations.Accessors + +/** + * Abstract probe implementation. A probe is thread-safe. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +abstract class AbstractProbe implements Probe { + + var manager : WeakReference + + var listeners : SmartListenerCollection + + @Accessors + val name : String + + @Accessors + val uri : URI + + @Accessors + val type : Class + + protected var active : boolean = true + + val lock : ReadWriteLock + + val listenerLock : ReadWriteLock + + val listenerCollectionProvider : Provider> + + + /** Constructor. + * + * @param manager the creator of this probe, and its manager. + * @param name the name of the probe + * @param uri the UI of the field. + * @param type the expected type of the probed value. + * @param listenerCollectionProvider the provider of listener collections. + * @param lockProvider the provider of synchronization locks. + */ + protected new ( + manager : IProbeReleaseListener, + name : String, + uri : URI, + type : Class, + listenerCollectionProvider : Provider>, + lockProvider : Provider) { + assert manager !== null + assert type !== null + assert listenerCollectionProvider !== null + assert lockProvider !== null + this.manager = new WeakReference(manager) + this.name = name + this.uri = uri + this.type = type + this.listenerCollectionProvider = listenerCollectionProvider + this.lock = lockProvider.get + this.listenerLock = lockProvider.get + } + + override getLock : ReadWriteLock { + this.lock + } + + /** Replies the lock object for listener list. */ + protected def getListenerLock : ReadWriteLock { + this.listenerLock + } + + @Pure + override isActive : boolean { + val lck = getLock + lck.readLock.lock + try { + return this.active + } finally { + lck.readLock.unlock + } + } + + override release { + var act : boolean + val lck = getLock + lck.readLock.lock + try { + act = this.active + } finally { + lck.readLock.unlock + } + if (act) { + var m : IProbeReleaseListener = null + try { + lck.writeLock.lock + try { + act = this.active + if (act) { + this.active = false + m = this.manager?.get + this.manager.clear + this.manager = null + } + } finally { + lck.writeLock.unlock + } + } finally { + fireRelease + var llck = getListenerLock + llck.writeLock.lock + try { + this.listeners = null + } finally { + llck.writeLock.unlock + } + } + if (m !== null) { + m.onProbeReleasedProbe(this) + } + } + } + + /** Notify the listener that the probe's value has changed. */ + protected def fireValueChanged { + var list : SmartListenerCollection + var lck = getListenerLock + lck.readLock.lock + try { + list = this.listeners + } finally { + lck.readLock.unlock + } + if (list !== null) { + list.notifyListeners(typeof(IProbeListener)) [ + probeValueChanged(this) + ] + } + } + + override addProbeListener(listener : IProbeListener) { + assert listener !== null + val lck = getListenerLock + lck.writeLock.lock + try { + if (this.listeners === null) { + this.listeners = this.listenerCollectionProvider.get + } + this.listeners.add(typeof(IProbeListener), listener) + } finally { + lck.writeLock.unlock + } + } + + override removeProbeListener(listener : IProbeListener) { + assert listener !== null + var list : SmartListenerCollection + val lck = getListenerLock + lck.readLock.lock + try { + list = this.listeners + } finally { + lck.readLock.unlock + } + if (list !== null) { + list.remove(typeof(IProbeListener), listener) + lck.writeLock.lock + try { + if (this.listeners !== null && this.listeners.isEmpty) { + this.listeners = null + } + } finally { + lck.writeLock.unlock + } + } + } + + /** Notify the listener that the probe has been released. + */ + protected def fireRelease { + var list : SmartListenerCollection + var lck = getListenerLock + lck.readLock.lock + try { + list = this.listeners + } finally { + lck.readLock.unlock + } + if (list !== null) { + list.notifyListeners(typeof(IProbeReleaseListener)) [ + onProbeReleasedProbe(this) + ] + } + } + + override addProbeReleaseListener(listener : IProbeReleaseListener) { + assert listener !== null + val lck = getListenerLock + lck.writeLock.lock + try { + if (this.listeners === null) { + this.listeners = this.listenerCollectionProvider.get + } + this.listeners.add(typeof(IProbeReleaseListener), listener) + } finally { + lck.writeLock.unlock + } + } + + override removeProbeReleaseListener(listener : IProbeReleaseListener) { + assert listener !== null + var list : SmartListenerCollection + val lck = getListenerLock + lck.readLock.lock + try { + list = this.listeners + } finally { + lck.readLock.unlock + } + if (list !== null) { + list.remove(typeof(IProbeReleaseListener), listener) + lck.writeLock.lock + try { + if (this.listeners !== null && this.listeners.isEmpty) { + this.listeners = null + } + } finally { + lck.writeLock.unlock + } + } + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/AbstractProbeService.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/AbstractProbeService.sarl new file mode 100644 index 0000000000..7933ae9dc2 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/AbstractProbeService.sarl @@ -0,0 +1,219 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.probing + +import io.sarl.lang.util.SynchronizedIterable +import io.sarl.sre.internal.SmartListenerCollection +import io.sarl.sre.naming.NameParser +import io.sarl.sre.naming.SarlName +import io.sarl.sre.services.AbstractSreService +import io.sarl.sre.services.namespace.FieldAccess +import io.sarl.sre.services.namespace.NamespaceService +import io.sarl.util.concurrent.Collections3 +import java.net.URI +import java.util.ArrayList +import java.util.Collection +import java.util.UUID +import java.util.concurrent.locks.ReadWriteLock +import javax.inject.Provider + +/** + * This class is the standard implementation of a probe service. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +abstract class AbstractProbeService extends AbstractSreService implements ProbeService, IProbeReleaseListener { + + val namespace : NamespaceService + + val nameParser : NameParser + + val probes = >newTreeMap(null) + + val listenerCollectionProvider : Provider> + + val lockProvider : Provider + + val lock : ReadWriteLock + + /** Constructor. + * + * @param namespaceService the service that is giving access to the namespaces. + * @param nameParser the object that is able to parse resource names. + * @param listenerCollectionProvider the provider of listener collections. + * @param lockProvider the provider of synchronization locks. + */ + protected new (namespaceService : NamespaceService, nameParser : NameParser, listenerCollectionProvider : Provider>, + lockProvider : Provider) { + this.namespace = namespaceService + this.nameParser = nameParser + this.listenerCollectionProvider = listenerCollectionProvider + this.lockProvider = lockProvider + this.lock = lockProvider.get + } + + /** Replies the synchronization lock. */ + @Pure + def getLock : ReadWriteLock { + this.lock + } + + protected override onStop { + releaseAllProbes + } + + override probe(valueName : String, valueType : Class, probeName : String = null) : Probe with T { + this.nameParser.decode(valueName).probe(valueType, probeName) + } + + override probe(valueName : URI, valueType : Class, probeName : String = null) : Probe with T { + var nuri = this.nameParser.normalize(valueName) + if (nuri !== null) { + return this.nameParser.decode(nuri).probe(valueType, probeName) + } + return null + } + + override probe(valueName : SarlName, valueType : Class, + probeName : String = null) : Probe with T { + assert valueType !== null + val uri = valueName.toURI + if (valueName === null) { + return null + } + var probe : Probe + var lck = getLock + lck.readLock.lock + try { + probe = this.probes.get(uri) as Probe + } finally { + lck.readLock.unlock + } + if (probe === null) { + var isFirst = false + lck.writeLock.lock + try { + probe = this.probes.get(uri) as Probe + if (probe === null) { + val probedValue = this.namespace.findObject(valueName) + val name = if(probeName.isNullOrEmpty) UUID::randomUUID.toString else probeName + probe = probedValue.newProbe(valueType, uri, name) + if (probe !== null) { + isFirst = this.probes.empty + this.probes.put(uri, probe) + } + } + } finally { + lck.writeLock.unlock + } + if (isFirst) { + onFirstProbe + } + } + return probe + } + + /** Invoked when the first probe is added into the service. + */ + protected def onFirstProbe { + // + } + + /** Invoked when the last probe is removed from the service. + */ + protected def onLastProbe { + // + } + + /** Create an instance of the probe. + * + * @param the expected type of the probed values. + * @param probedElement the element to which the probe must be associated, never {@code null}. + * @param valueType the expected type of the probed values, never {@code null}. + * @param probeUri the URI of the probe, never {@code null}. + * @param probeName the name of the probe, never {@code null}. + */ + protected def newProbe(probedElement : Object, valueType : Class, probeUri : URI, probeName : String) : Probe with T { + if (probedElement instanceof FieldAccess) { + return new FieldProbe(this, probeName, probeUri, valueType, probedElement, this.listenerCollectionProvider, + this.lockProvider) + } + return null + } + + override getProbes : SynchronizedIterable> { + var lck = getLock + lck.readLock.lock + try { + return Collections3::unmodifiableSynchronizedIterable(this.probes.values, lck) + } finally { + lck.readLock.unlock + } + } + + override releaseAllProbes { + var removable : Collection> + var lck = getLock + lck.readLock.lock + try { + // Copy of the probes into a separate array is mandatory + // for avoiding ConcurrentModificationException + removable = new ArrayList(this.probes.values) + } finally { + lck.readLock.unlock + } + removable.forEach [ + it.release + ] + } + + override sync { + var lck = getLock + lck.readLock.lock + try { + this.probes.values.parallelStream.forEach [ + it.sync + ] + } finally { + lck.readLock.unlock + } + } + + override onProbeReleasedProbe(probe : Probe) { + var isLast = false + var lck = getLock + lck.writeLock.lock + try { + this.probes.remove(probe.uri) + isLast = this.probes.empty + } finally { + lck.writeLock.unlock + } + if (isLast) { + onLastProbe + } + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/AsynchronousProbeService.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/AsynchronousProbeService.sarl new file mode 100644 index 0000000000..01a9185118 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/AsynchronousProbeService.sarl @@ -0,0 +1,121 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.probing + +import io.sarl.sre.internal.SmartListenerCollection +import io.sarl.sre.naming.NameParser +import io.sarl.sre.services.executor.ExecutorService +import io.sarl.sre.services.executor.Runnables +import io.sarl.sre.services.namespace.NamespaceService +import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.locks.ReadWriteLock +import javax.inject.Inject +import javax.inject.Provider +import org.eclipse.xtend.lib.annotations.Accessors + +/** + * This class is the implementation of a probe service that is updating the values asynchronously. + * Asynchronous updates means that the {@link #sync()} function is invoking by a specific thread. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + * @see SynchronousProbeService + */ +class AsynchronousProbeService extends AbstractProbeService { + + val executor : ExecutorService + + val synchronizationStarted = new AtomicBoolean(false) + + val enableSynchronization = new AtomicBoolean(true) + + @Accessors + volatile var sleepingDurationBetweenSyncs : long = 500l + + /** Constructor. + * + * @param service the executor service. + * @param namespaceService the service that is giving access to the namespaces. + * @param nameParser the object that is able to parse resource names. + * @param listenerCollectionProvider the provider of listener collections. + * @param lockProvider the provider of synchronization locks. + */ + @Inject + new (service : ExecutorService, namespaceService : NamespaceService, nameParser : NameParser, + listenerCollectionProvider : Provider>, + lockProvider : Provider) { + super(namespaceService, nameParser, listenerCollectionProvider, lockProvider) + this.executor = service + } + + protected override onFirstProbe { + startSyncThread + } + + protected override onLastProbe { + stopSyncThread + } + + /** Start the thread that dynamically update the probes */ + def startSyncThread { + if (!this.synchronizationStarted.getAndSet(true)) { + var task = Runnables::named(createRunnableUpdater, "Probe Updater") + this.executor.executeAsap(task) + } + } + + /** Invoked to create a {@code Runnable} that may be used for updated the probes + * + * @return the {@code Runnable} insstance, never {@code null}. + */ + @SuppressWarnings("discouraged_reference") + protected def createRunnableUpdater : Runnable { + [ + this.enableSynchronization.set(true) + while (this.enableSynchronization.get) { + sync + val duration = getSleepingDurationBetweenSyncs + if (duration <= 0l) { + Thread::yield + } else { + Thread::sleep(duration) + } + } + ] + } + + /** Stop the thread that dynamically update the probes + */ + @SuppressWarnings("discouraged_reference") + def stopSyncThread { + this.enableSynchronization.set(false) + this.synchronizationStarted.set(false) + } + + protected override onStop { + stopSyncThread + super.onStop + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/FieldProbe.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/FieldProbe.sarl new file mode 100644 index 0000000000..e33a16100e --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/FieldProbe.sarl @@ -0,0 +1,200 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.probing + +import io.sarl.sre.internal.SmartListenerCollection +import io.sarl.sre.services.namespace.FieldAccess +import java.net.URI +import java.util.concurrent.locks.ReadWriteLock +import javax.inject.Provider + +/** + * Probe implementation is linked to a field. A probe is thread-safe. + * This probe buferrizes the values. In order to be updated, the + * {@link #sync()} function must be invoked. Then, the new value is read + * from the associated field, and the probe listeners are notified. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +class FieldProbe extends AbstractFieldProbe { + + var in = false + + var init = true + + var inbuffer : T + + var out = false + + var outbuffer : T + + /** Constructor. + * + * @param manager the creator of this probe, and its manager. + * @param name the name of the probe + * @param uri the UI of the field. + * @param type the expected type of the probed value. + * @param field the accessor to the probed field. + * @param listenerCollectionProvider the provider of listener collections. + * @param lockProvider the provider of synchronization locks. + */ + protected new (manager : IProbeReleaseListener, name : String, uri : URI, type : Class, field : FieldAccess, + listenerCollectionProvider : Provider>, lockProvider : Provider) { + super(manager, name, uri, type, field, listenerCollectionProvider, lockProvider) + } + + @Pure + override isInvalid : boolean { + var lck = getLock + lck.readLock.lock + try { + if (active) { + return this.in || this.out + } + } finally { + lck.readLock.unlock + } + return false + } + + override setValue(value : T) { + if (isActive) { + var lck = getLock + lck.writeLock.lock + try { + this.out = true + this.outbuffer = value + } finally { + lck.writeLock.unlock + } + } + } + + @Pure + override getValue : T { + if (isActive && syncIn) { + fireValueChanged + } + return this.inbuffer + } + + /** Do input synchronization. + * + *

This function is thread-safe. + */ + private def syncIn : boolean { + var continueSync : boolean + var lck = getLock + lck.readLock.lock + try { + continueSync = this.in || this.init + } finally { + lck.readLock.unlock + } + if (continueSync) { + lck.writeLock.lock + try { + val enableChange = !this.init + this.in = false + this.init = false + var oldValue = this.inbuffer + var newValue = readField + this.inbuffer = newValue + return enableChange && newValue != oldValue + } finally { + lck.writeLock.unlock + } + } + return false + } + + /** Do output synchronization. + * + *

This function is thread-safe. + */ + private def syncOut : boolean { + var continueSync : boolean + var lck = getLock + lck.readLock.lock + try { + continueSync = this.out + } finally { + lck.readLock.unlock + } + if (continueSync) { + lck.writeLock.lock + try { + this.out = false + var newValue = this.outbuffer + this.outbuffer = null + var oldValue = writeField(newValue) + return newValue != oldValue + } finally { + lck.writeLock.unlock + } + } + return false + } + + /** Force the synchronization of the probed value. + */ + def sync(forcedReading : boolean) { + if (isActive) { + if (isActiveObject) { + val changed0 = syncOut + var lck = getLock + lck.writeLock.lock + try { + this.in = forcedReading || this.in + } finally { + lck.writeLock.unlock + } + val changed1 = syncIn + if (changed0 || changed1) { + fireValueChanged + } + } else { + release + } + } + } + + override release { + if (isActive) { + var lck = getLock + lck.writeLock.lock + try { + // Caution: Do not reset the inbuffer field in order to enable access to the value even if the probe is inactive + this.in = false + this.out = false + this.outbuffer = null + super.release + } finally { + lck.writeLock.unlock + } + } + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/IProbeListener.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/IProbeListener.sarl new file mode 100644 index 0000000000..05092f21e6 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/IProbeListener.sarl @@ -0,0 +1,42 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.probing + +import java.util.EventListener + +/** + * Listener on probe changes. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +interface IProbeListener extends EventListener { + + /** Invoked when the value of a probe has changed. + * + * @param probe the probe that has changed. + */ + def probeValueChanged(probe : Probe) + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/IProbeReleaseListener.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/IProbeReleaseListener.sarl new file mode 100644 index 0000000000..f69efdc237 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/IProbeReleaseListener.sarl @@ -0,0 +1,42 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.probing + +import java.util.EventListener + +/** + * Listener on probe releases. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +interface IProbeReleaseListener extends EventListener { + + /** Invoked when a probe was released. + * + * @param probe is the released probe. + */ + def onProbeReleasedProbe(probe : Probe) + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/Probe.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/Probe.sarl new file mode 100644 index 0000000000..6adaa65d0a --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/Probe.sarl @@ -0,0 +1,109 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.probing + +import java.net.URI +import java.util.concurrent.locks.ReadWriteLock + +/** + * Probe implementation. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +interface Probe { + + /** Replies the object that could be used to be synchronized with this probe. */ + @Pure + def getLock : ReadWriteLock + + /** Replies the name of the probe + */ + @Pure + def getName : String + + /** Replies the probed component. + */ + @Pure + def getUri : URI + + /** Replies the probed value. + */ + @Pure + def getValue : T + + /** The type of the value. + */ + @Pure + def getType : Class + + /** Change the probed value. + * + * @param v the new value. + */ + def setValue(v : T) + + /** Force the synchronization of the probed value. + * + * @param forceReading indicates if the reading of the probed value must be forced from the probed element. + * By default, the value of this parameter is {@code true}. + */ + def sync(forceReading : boolean = true) + + /** Releases any resource associated to the probe. + */ + def release + + /** Add listener on probe changes. */ + def addProbeListener(listener : IProbeListener) + + /** Remove listener on probe changes. + */ + def removeProbeListener(listener : IProbeListener) + + /** Add listener on probe release. + */ + def addProbeReleaseListener(listener : IProbeReleaseListener) + + /** Remove listener on probe release. + */ + def removeProbeReleaseListener(listener : IProbeReleaseListener) + + /** Replies if this probe is active. When a probe is active, it could + * be synchronized to the probed element. + * + * @return {@code true} if the probe is active. + */ + @Pure + def isActive : boolean + + /** Replies if this probe is invalid. When a probe is invalid, + * the exhibited value may not corresponds to the probed element's value. + * + * @return {@code true} if the probe is invalid. + */ + @Pure + def isInvalid : boolean + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/ProbeService.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/ProbeService.sarl new file mode 100644 index 0000000000..b2245ed1b4 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/ProbeService.sarl @@ -0,0 +1,88 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.probing + +import com.google.common.util.concurrent.Service +import io.sarl.lang.util.SynchronizedIterable +import io.sarl.sre.naming.SarlName +import java.net.URI + +/** + * This service provides probing mechanisms. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +interface ProbeService extends Service { + + /** Create a probe. + * + *

If a probe with the same name name exists, a composed probe with the probe name is created with + * the probes as composite. + * + * @param valueName the probed element. + * @param valueType the expected type of the value. + * @param probeName is the optional name of the probe. + */ + def probe(valueName : SarlName, valueType : Class, probeName : String = null) : Probe with T + + /** Create a probe. + * + *

If a probe with the same name name exists, a composed probe with the probe name is created with + * the probes as composite. + * + * @param valueName the probed element. + * @param valueType the expected type of the value. + * @param probeName is the optional name of the probe. + */ + def probe(valueName : URI, valueType : Class, probeName : String = null) : Probe with T + + /** Create a probe. + * + *

If a probe with the same name name exists, a composed probe with the probe name is created with + * the probes as composite. + * + * @param valueName the probed element. + * @param valueType the expected type of the value. + * @param probeName is the optional name of the probe. + */ + def probe(valueName : String, valueType : Class, probeName : String = null) : Probe with T + + + /** Replies all the probes. + * + * @return the iterable on probes + */ + @Pure + def getProbes : SynchronizedIterable> + + /** Release all the probes. + */ + def releaseAllProbes + + /** Force the synchronization of the probes. + */ + def sync + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/SynchronousProbeService.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/SynchronousProbeService.sarl new file mode 100644 index 0000000000..1905735bee --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/probing/SynchronousProbeService.sarl @@ -0,0 +1,57 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.probing + +import io.sarl.sre.internal.SmartListenerCollection +import io.sarl.sre.naming.NameParser +import io.sarl.sre.services.namespace.NamespaceService +import java.util.concurrent.locks.ReadWriteLock +import javax.inject.Inject +import javax.inject.Provider + +/** + * This class is the implementation of a probe service that is updating the values synchronously. + * Synchronous updates must be explicitly started by invoking {@link #sync()}. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + * @see AsynchronousProbeService + */ +class SynchronousProbeService extends AbstractProbeService { + + /** Constructor. + * + * @param namespaceService the service that is giving access to the namespaces. + * @param nameParser the object that is able to parse resource names. + * @param listenerCollectionProvider the provider of listener collections. + * @param lockProvider the provider of synchronization locks. + */ + @Inject + new (namespaceService : NamespaceService, nameParser : NameParser, + listenerCollectionProvider : Provider>, + lockProvider : Provider) { + super(namespaceService, nameParser, listenerCollectionProvider, lockProvider) + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/time/AbstractTimeService.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/time/AbstractTimeService.sarl new file mode 100644 index 0000000000..08f0cae345 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/time/AbstractTimeService.sarl @@ -0,0 +1,84 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.time + +import io.sarl.sre.internal.SmartListenerCollection +import io.sarl.sre.services.AbstractSreService + +/** + * Time service based on the JRE standard time. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10.0 + */ +abstract class AbstractTimeService extends AbstractSreService implements TimeService { + + val listeners : SmartListenerCollection + + /** Constructor. + * + * @param listeners the empty collection of listeners that must be used by this service. + */ + new (listeners : SmartListenerCollection) { + assert listeners !== null + this.listeners = listeners + } + + def getOSCurrentTime : long { + System::currentTimeMillis + } + + def fromOSDuration(timeDuration : double) : double { + timeDuration / OSTimeFactor + } + + def fromOSTime(timeValue : double) : double { + timeValue / OSTimeFactor + } + + def toOSDuration(timeDuration : double) : double { + timeDuration * OSTimeFactor + } + + def toOSTime(timeValue : double) : double { + timeValue * OSTimeFactor + } + + /** Notifies about time changes. + */ + protected def fireTimeChanged { + this.listeners.notifyListeners(typeof(TimeListener)) [ + it.timeChanged(this) + ] + } + + def addTimeListener(listener : TimeListener) { + this.listeners.add(typeof(TimeListener), listener) + } + + def removeTimeListener(listener : TimeListener) { + this.listeners.remove(typeof(TimeListener), listener); + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/time/JreTimeService.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/time/JreTimeService.sarl new file mode 100644 index 0000000000..e94fcdd6ba --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/time/JreTimeService.sarl @@ -0,0 +1,79 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.time + +import java.util.concurrent.TimeUnit +import javax.inject.Singleton +import javax.inject.Inject +import io.sarl.sre.internal.SmartListenerCollection + +/** + * Time service based on the JRE standard time. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.6.0 + */ +@Singleton +class JreTimeService extends AbstractTimeService { + + /** Constructor. + * + * @param listeners the empty collection of listeners that must be used by this service. + */ + @Inject + new (listeners : SmartListenerCollection) { + super(listeners) + } + + def getOSCurrentTime : long { + System::currentTimeMillis + } + + def getTimePrecision : TimeUnit { + TimeUnit::MILLISECONDS + } + + def getTime(timeUnit : TimeUnit) : double { + val currentTime = this.OSCurrentTime + val tu = timeUnit ?: TimeUnit::SECONDS + if (tu == TimeUnit::MILLISECONDS) { + return currentTime + } + return currentTime.convertFromTo(TimeUnit::MILLISECONDS, tu) + } + + def getOSTimeFactor : double { + 1.0 + } + + def evolveTimeIfPossible(timeDelta : double) : boolean { + fireTimeChanged + return false + } + + def setTimeIfPossible(time : double) : boolean { + false + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/time/TimeService.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/time/TimeService.sarl new file mode 100644 index 0000000000..a8891e6537 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/services/time/TimeService.sarl @@ -0,0 +1,157 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.services.time + +import com.google.common.util.concurrent.Service +import java.util.EventListener +import java.util.concurrent.TimeUnit + +/** + * This class enables to have access to the time manager of the platform. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.6.0 + */ +interface TimeService extends Service { + + /** Replies the current time. + * + * @param timeUnit if present, specifies the time unit of the value to reply. By default is it seconds. + * @return the current time. + */ + def getTime(timeUnit : TimeUnit) : double + + /** Replies the OS current time. + * + * @return the OS current time in ms. + */ + def getOSCurrentTime : long + + /** Replies the scaling factor between the agent time and the operating system time. + *

Consider time in the agent application. It may evolves at a different rate than + * the operating system time. The value that is replied by this function permits to + * determine the proportionally between the agent application time and the operating system time. + * A typical equation for representing this relation is: os time = OSTimeFactor * agent time. + * + * @return the factor between agent time and operating system time. + */ + def getOSTimeFactor : double + + /** Convert the given duration from the operating system time scale to the agent system time scale. + * This function uses {@link #getOSTimeFactor()} for converting the duration. + * + * @param timeDuration the duration for the operating system. + * @return the duration for the agent system. + * @since 0.9 + */ + def fromOSDuration(timeDuration : double) : double + + /** Convert the given time from the operating system time scale to the agent system time scale. + * This function uses {@link #getOSTimeFactor()} for converting the duration. + * + * @param timeValue the time for the operating system. + * @return the time for the agent system. + * @since 0.9 + */ + def fromOSTime(timeValue : double) : double + + /** Convert the given duration from the agent system time scale to the operating system time scale. + * This function uses {@link #getOSTimeFactor()} for converting the duration. + * + * @param timeDuration the duration for the agent system. + * @return the duration for the operating system. + * @since 0.9 + */ + def toOSDuration(timeDuration : double) : double + + /** Convert the given time from the agent system time scale to the operating system time scale. + * This function uses {@link #getOSTimeFactor()} for converting the time. + * + * @param timeValue the time for the agent system. + * @return the time for the operating system. + * @since 0.9 + */ + def toOSTime(timeValue : double) : double + + /** Replies the precision of the time that is assumed by this manager. + * + *

Any duration that is below the time precision is ignored. + * + * @return the time precision, never {@code null}. + */ + def getTimePrecision : TimeUnit + + /** Do a time evolution if the underlying service allows this action. + * + * @param timeDelta is the amount of time to consider for the evolution. If it is negative or zero, + * a default amount will be selected by the time service. + * @return {@code true} if time has evolved. Otherwise {@code false}. + */ + def evolveTimeIfPossible(timeDelta : double = 0.0) : boolean + + /** Change the time. + * + * @param time the new time. + * @return {@code true} if time has evolved. Otherwise {@code false}. + */ + def setTimeIfPossible(time : double) : boolean + + /** Add listener on time changes. + * + * @param listener the listener. + */ + def addTimeListener(listener : TimeListener) + + /** Remove listener on time changes. + * + * @param listener the listener. + */ + def removeTimeListener(listener : TimeListener) + +} + +/** + * Listener on time changes into the time service. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.6.0 + */ +interface TimeListener extends EventListener { + + /** Invoked when the time has changed into the time manager. + * + * @param service the time service that changed. + */ + def timeChanged(service : TimeService) + + /** Invoked when the time unit has changed into the time manager. + * + * @param service the time service that changed. + */ + def timeUnitChanged(service : TimeService) + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/BuiltinSkill.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/BuiltinSkill.sarl new file mode 100644 index 0000000000..ec51de52c8 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/BuiltinSkill.sarl @@ -0,0 +1,46 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.skills + +import io.sarl.lang.core.Skill +import java.util.concurrent.locks.ReadWriteLock +import javax.inject.Provider + +/** + * A builtin skill into the SRE. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +abstract class BuiltinSkill extends Skill { + + /** Provider of locks. */ + protected val lockProvider : Provider + + /** Constructor. + */ + new (provider : Provider) { + this.lockProvider = provider + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/SreDynamicSkillProvider.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/SreDynamicSkillProvider.sarl new file mode 100644 index 0000000000..856ac16fe9 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/SreDynamicSkillProvider.sarl @@ -0,0 +1,151 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.skills + +import com.google.inject.Injector +import io.sarl.core.Behaviors +import io.sarl.core.DefaultContextInteractions +import io.sarl.core.ExternalContextAccess +import io.sarl.core.InnerContextAccess +import io.sarl.core.Lifecycle +import io.sarl.core.Logging +import io.sarl.core.Schedules +import io.sarl.core.Time +import io.sarl.lang.annotation.PrivateAPI +import io.sarl.lang.core.Agent +import io.sarl.lang.core.Capacity +import io.sarl.lang.core.DynamicSkillProvider +import io.sarl.lang.core.SREutils +import io.sarl.lang.core.Skill +import io.sarl.lang.util.ClearableReference +import io.sarl.sre.capacities.InternalEventBusCapacity +import io.sarl.sre.capacities.InternalSchedules +import io.sarl.sre.capacities.MicroKernelCapacity +import io.sarl.sre.skills.bic.BehaviorsSkill +import io.sarl.sre.skills.bic.DefaultContextInteractionsSkill +import io.sarl.sre.skills.bic.ExternalContextAccessSkill +import io.sarl.sre.skills.bic.InnerContextAccessSkill +import io.sarl.sre.skills.bic.LifecycleSkill +import io.sarl.sre.skills.bic.LoggingSkill +import io.sarl.sre.skills.bic.SchedulesSkill +import io.sarl.sre.skills.bic.TimeSkill +import io.sarl.sre.skills.internal.InternalEventBusSkill +import io.sarl.sre.skills.internal.MicroKernelSkill +import javax.inject.Inject +import javax.inject.Singleton + +/** + * Provider of the built-in capacities of the SRE platform. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.6.0 + */ +@SuppressWarnings("use_reserved_sarl_annotation") +@PrivateAPI(isCallerOnly = true) +@Singleton +class SreDynamicSkillProvider implements DynamicSkillProvider { + + val injector : Injector + + /** Constructor. + * + * @param injector the injector. + */ + @Inject + new (injector : Injector) { + this.injector = injector + } + + /** Create the skill instance as fast as possible. */ + protected def newSkillInstance(^capacity : Class) : Pair> { + switch (^capacity) { + case typeof(InternalEventBusCapacity): { + var ^skill = this.injector.getInstance(typeof(InternalEventBusSkill)) + return Pair::of(^skill, null) + } + case typeof(DefaultContextInteractions): { + var ^skill = this.injector.getInstance(typeof(DefaultContextInteractionsSkill)) + return Pair::of(^skill, null) + } + case typeof(Lifecycle): { + var ^skill = this.injector.getInstance(typeof(LifecycleSkill)) + return Pair::of(^skill, null) + } + case typeof(Schedules): { + var ^skill = this.injector.getInstance(typeof(SchedulesSkill)) + return Pair::of(^skill, typeof(InternalSchedules)) + } + case typeof(InternalSchedules): { + var ^skill = this.injector.getInstance(typeof(SchedulesSkill)) + return Pair::of(^skill, typeof(Schedules)) + } + case typeof(Behaviors): { + var ^skill = this.injector.getInstance(typeof(BehaviorsSkill)) + return Pair::of(^skill, null) + } + case typeof(Logging): { + var ^skill = this.injector.getInstance(typeof(LoggingSkill)) + return Pair::of(^skill, null) + } + case typeof(Time): { + var ^skill = this.injector.getInstance(typeof(TimeSkill)) + return Pair::of(^skill, null) + } + case typeof(InnerContextAccess): { + var ^skill = this.injector.getInstance(typeof(InnerContextAccessSkill)) + return Pair::of(^skill, null) + } + case typeof(ExternalContextAccess): { + var ^skill = this.injector.getInstance(typeof(ExternalContextAccessSkill)) + return Pair::of(^skill, null) + } + case typeof(MicroKernelCapacity): { + var ^skill = this.injector.getInstance(typeof(MicroKernelSkill)) + return Pair::of(^skill, null) + } + } + return null + } + + def installSkill(^agent : Agent, ^capacity : Class) : ClearableReference { + var pair = ^capacity.newSkillInstance + var ^skill : Skill = pair?.key + if (^skill !== null) { + var additionalCapacity = pair.value + if (additionalCapacity !== null) { + return ^agent.registerSkill(#[^capacity, additionalCapacity], ^skill) + } + return ^agent.registerSkill(#[^capacity], ^skill) + } + return null + } + + /** Register the given skill. + */ + protected def registerSkill(^agent : Agent, capacities : Class[], + ^skill : Skill) : ClearableReference { + SREutils::setInternalSkill(^agent, ^skill, capacities) + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/BehaviorsSkill.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/BehaviorsSkill.sarl new file mode 100644 index 0000000000..2c06c5de27 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/BehaviorsSkill.sarl @@ -0,0 +1,147 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.skills.bic + +import io.sarl.core.Behaviors +import io.sarl.core.Destroy +import io.sarl.core.Initialize +import io.sarl.lang.core.Address +import io.sarl.lang.core.Behavior +import io.sarl.lang.core.Event +import io.sarl.lang.core.EventListener +import io.sarl.lang.core.Scope +import io.sarl.lang.util.SynchronizedIterable +import io.sarl.sre.capacities.InternalEventBusCapacity +import io.sarl.sre.capacities.InternalSchedules +import io.sarl.sre.skills.BuiltinSkill +import java.util.concurrent.locks.ReadWriteLock +import javax.inject.Inject +import javax.inject.Provider + +import static io.sarl.sre.services.lifecycle.AgentLife.* + +/** + * SRE implementation of SARL's {@link Behaviors} built-in capacity. + * + * @author $Author: srodriguez$ + * @author $Author: ngaud$ + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +skill BehaviorsSkill extends BuiltinSkill implements Behaviors { + + uses InternalEventBusCapacity, InternalSchedules + + /** Constructor. */ + @Inject + new (lockProvider : Provider) { + super(lockProvider) + } + + protected override uninstall(stage : UninstallationStage) { + if (stage === UninstallationStage::PRE_DESTROY_EVENT) { + val adr = getLife(owner, this.lockProvider).addressInInnerDefaultSpace + unregisterEventBusListener(typeof(Behavior)) [ + // Destroy the behavior + val ^event = new Destroy + ^event.source = adr + fireEventAndWait(^event, false, true, it) + (it as Behavior).releaseInternalResources + ] + } + } + + def asEventListener : EventListener { + associatedEventBusListener + } + + def registerBehavior(attitude : Behavior, filter : (Event)=>boolean, initializationParameters : Object*) : Behavior { + assert attitude !== null + val life = getLife(owner, this.lockProvider) + if (life.state.alive) { + val adr = life.addressInInnerDefaultSpace + attitude.registerEventBusListener(filter) [ + // Initialize the behavior + val ^event = new Initialize(this.ID, initializationParameters) + ^event.source = adr + fireEventAndWait(^event, false, true, attitude) + ] + } + return attitude + } + + def unregisterBehavior(attitude : Behavior) : Behavior { + assert attitude !== null + var adr = getLife(owner, this.lockProvider).addressInInnerDefaultSpace + attitude.unregisterTasksForBehavior + attitude.unregisterEventBusListener [ + // Destroy the behavior + val ^event = new Destroy + ^event.source = adr + fireEventAndWait(^event, false, true, attitude) + attitude.releaseInternalResources + ] + return attitude + } + + def wake(^event : Event, scope : Scope

= null) { + // Use the inner space so all behaviors (even agents inside the holon + // running in distant kernels) are notified. The event will return into + // the agent via the inner default space add call internalReceiveEvent + // for real posting + val life = getLife(owner, this.lockProvider) + if (life.state.alive) { + var context = life.innerContext + if (context !== null) { + val defSpace = context.defaultSpace + ^event.source = life.addressInInnerDefaultSpace + defSpace.emit(ID, ^event, scope) + } else { + // Do not call getInnerContext(), which is creating the inner context automatically. + // In place, try to send the event inside the agent only (and its behaviors). + val adr = life.addressInInnerDefaultSpace + if (scope === null || scope.matches(adr)) { + val listener = associatedEventBusListener + assert listener !== null + ^event.source = adr + listener.receiveEvent(^event) + } + } + } + } + + def getRegisteredBehaviors : SynchronizedIterable { + getRegisteredEventBusListeners(typeof(Behavior)) + } + + def hasRegisteredBehavior : boolean { + val iterable = getRegisteredEventBusListeners(typeof(Behavior)) + iterable.lock.readLock.lock + try { + return iterable.iterator.hasNext + } finally { + iterable.lock.readLock.unlock + } + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/DefaultContextInteractionsSkill.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/DefaultContextInteractionsSkill.sarl new file mode 100644 index 0000000000..fd1ffd8f92 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/DefaultContextInteractionsSkill.sarl @@ -0,0 +1,118 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.skills.bic + +import io.sarl.core.DefaultContextInteractions +import io.sarl.lang.core.Address +import io.sarl.lang.core.Agent +import io.sarl.lang.core.AgentContext +import io.sarl.lang.core.Event +import io.sarl.lang.core.EventSpace +import io.sarl.lang.core.Scope +import io.sarl.lang.core.Space +import io.sarl.lang.core.SpaceID +import io.sarl.sre.skills.BuiltinSkill +import java.util.UUID +import java.util.concurrent.locks.ReadWriteLock +import javax.inject.Inject +import javax.inject.Provider + +import static io.sarl.sre.services.lifecycle.AgentLife.* + +/** + * Skill to access to the default interaction context. + * + * @author $Author: srodriguez$ + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +skill DefaultContextInteractionsSkill extends BuiltinSkill implements DefaultContextInteractions { + + /** Constructor. */ + @Inject + new (lockProvider : Provider) { + super(lockProvider) + } + + def getDefaultContext : AgentContext { + getLife(owner, this.lockProvider).defaultContext.context + } + + def getDefaultSpace : EventSpace { + getLife(owner, this.lockProvider).defaultContext.defaultSpace + } + + def getDefaultAddress : Address { + getLife(owner, this.lockProvider).defaultContext.addressInDefaultSpace + } + + def isDefaultContext(context : AgentContext) : boolean { + context.ID.isDefaultContext + } + + def isDefaultContext(contextID : UUID) : boolean { + contextID == this.defaultContext.ID + } + + def isDefaultSpace(^space : Space) : boolean { + ^space.spaceID.isDefaultSpace + } + + def isDefaultSpace(^space : SpaceID) : boolean { + ^space == this.defaultSpace.spaceID + } + + def isDefaultSpace(^space : UUID) : boolean { + ^space == this.defaultSpace.spaceID.ID + } + + def isInDefaultSpace(^event : Event) : boolean { + if (^event !== null) { + val adr = ^event.source + if (adr !== null) { + return adr.spaceID.isDefaultSpace + } + } + return false + } + + def emit(^event : Event, scope : Scope
= null) { + ^event.source = this.defaultAddress + this.defaultSpace.emit(ID, ^event, scope) + } + + def willReceive(receiver : UUID, ^event : Event) { + ^event.emit [it.UUID == receiver] + } + + @Deprecated + def receive(receiver : UUID, ^event : Event) { + receiver.willReceive(^event) + } + + @Deprecated + def spawn(agentType : Class, params : Object*) : UUID { + throw new UnsupportedOperationException("Use Lifecycle capacity") + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/ExternalContextAccessSkill.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/ExternalContextAccessSkill.sarl new file mode 100644 index 0000000000..76fcf2be32 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/ExternalContextAccessSkill.sarl @@ -0,0 +1,246 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.skills.bic + +import io.sarl.core.ExternalContextAccess +import io.sarl.lang.core.Address +import io.sarl.lang.core.AgentContext +import io.sarl.lang.core.Event +import io.sarl.lang.core.EventSpace +import io.sarl.lang.core.Scope +import io.sarl.lang.core.Space +import io.sarl.lang.core.SpaceID +import io.sarl.lang.util.SynchronizedIterable +import io.sarl.sre.KernelScope +import io.sarl.sre.capacities.InternalEventBusCapacity +import io.sarl.sre.services.context.ContextService +import io.sarl.sre.services.context.ExternalContextMemberListener +import io.sarl.sre.services.context.InternalContextMembershipListener +import io.sarl.sre.services.context.InternalContextMembershipListenerFactory +import io.sarl.sre.services.lifecycle.ContextReference +import io.sarl.sre.skills.BuiltinSkill +import io.sarl.util.concurrent.Collections3 +import java.util.UUID +import java.util.concurrent.locks.ReadWriteLock +import javax.inject.Inject +import javax.inject.Provider + +import static io.sarl.sre.services.lifecycle.AgentLife.* + +/** + * Skill that permits to access to the context in which the agent is located. + * + * @author $Author: srodriguez$ + * @author $Author: ngaud$ + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +@SuppressWarnings("potential_field_synchronization_problem") +skill ExternalContextAccessSkill extends BuiltinSkill implements ExternalContextAccess { + + uses InternalEventBusCapacity + + val contextService : ContextService + + val externalEventEmitter : ExternalContextMemberListener + + val innerContextProvider : InternalContextMembershipListenerFactory + + var internalEventEmitter : InternalContextMembershipListener + + /** Constructor. */ + @Inject + new (service : ContextService, @KernelScope outerContextProvider : Provider, + @KernelScope innerContextProvider : InternalContextMembershipListenerFactory, + lockProvider : Provider) { + super(lockProvider) + this.contextService = service + this.externalEventEmitter = outerContextProvider.get + this.innerContextProvider = innerContextProvider + } + + protected override install { + this.internalEventEmitter = this.innerContextProvider.create(owner, this.lockProvider) + } + + protected override uninstall(stage : UninstallationStage) { + if (stage == UninstallationStage::POST_DESTROY_EVENT) { + + var life = getLife(owner, this.lockProvider) + // Unregister from the external context. + // The unregistration to the default context is be done into the AgentLife class. + var contexts = life.externalContexts + var identifiers = newArrayList + val lck = contexts.lock + lck.readLock.lock + try { + identifiers += contexts.map[it.context.ID] + } finally { + lck.readLock.unlock + } + for (id : identifiers) { + id.leave + } + } + } + + def getUniverseContext : AgentContext { + this.contextService.rootContext + } + + def getContext(contextID : UUID) : AgentContext { + assert contextID !== null + var life = getLife(owner, this.lockProvider) + var defaultContext = life.defaultContext + if (defaultContext !== null) { + var theDefaultContext = defaultContext.context + if (theDefaultContext !== null && contextID == theDefaultContext.ID) { + return theDefaultContext + } + } + var context = life.getExternalContext(contextID) + if (context === null) { + return null + } + return context.context + } + + def getAllContexts : SynchronizedIterable { + var collection = getLife(owner, this.lockProvider).enclosingContexts + var col2 = collection.map[var local : AgentContext = it.context; local] + Collections3::unmodifiableSynchronizedIterable(col2, collection.lock) + } + + def isInSpace(^event : Event, ^space : Space) : boolean { + ^event.isInSpace(^space.spaceID) + } + + def isInSpace(^event : Event, spaceID : SpaceID) : boolean { + spaceID == ^event.source.spaceID + } + + def isInSpace(^event : Event, spaceID : UUID) : boolean { + spaceID == ^event.source.spaceID.ID + } + + def join(contextID : UUID, expectedDefaultSpaceID : UUID) : boolean { + assert contextID !== null + assert expectedDefaultSpaceID !== null + + var life = getLife(owner, this.lockProvider) + if (life.state.alive) { + if (life.defaultContext.context.ID == contextID) { + return false + } + var existingContext = life.getExternalContext(contextID) + if (existingContext !== null) { + return false + } + + var context = this.contextService.getContext(contextID) + if (context === null) { + return false + } + + val defaultSpace = context.defaultSpace + val defaultSpaceID = defaultSpace.spaceID + + if (expectedDefaultSpaceID != defaultSpaceID.ID) { + return false + } + + val agentID = ID + val adr = new Address(defaultSpaceID, agentID) + life.addExternalContext(context, adr) + + defaultSpace.register(associatedEventBusListener) + + this.externalEventEmitter.memberJoined( + context, defaultSpaceID, + agentID, this.owner.class.name) + this.internalEventEmitter.contextJoined(contextID, expectedDefaultSpaceID) + + return true + } + return false + } + + def leave(contextID : UUID) : boolean { + assert contextID !== null + + var life = getLife(owner, this.lockProvider) + var exitDefaultSpace = false + var context : ContextReference + var candidate : ContextReference + if (life.defaultContext.context.ID == contextID) { + // Special case: an agent must always be into a default space. + if (life.externalContextCount != 1) { + return false + } + var ctxs = life.externalContexts + val lck = ctxs.lock + lck.readLock.lock + try { + var iterator = ctxs.iterator + if (!iterator.hasNext) { + return false + } + candidate = iterator.next + } finally { + lck.readLock.lock + } + context = life.defaultContext + exitDefaultSpace = true + } else { + context = life.getExternalContext(contextID) + } + + if (context === null) { + return false + } + + assert contextID == context.context.ID + assert contextID == context.addressInDefaultSpace.spaceID.contextID + + context.defaultSpace.unregister(associatedEventBusListener) + + if (exitDefaultSpace) { + assert candidate !== null + life.setDefaultContext(candidate.context, candidate.addressInDefaultSpace) ?: context + } else { + life.removeExternalContext(context) + } + // To send this event the agent must still be inside the context and its default space + this.internalEventEmitter.contextLeft(contextID) + this.externalEventEmitter.memberLeft( + context.context, context.addressInDefaultSpace.spaceID, + this.owner.ID, this.owner.class.name) + + return true + } + + def emit(^space : EventSpace, ^event : Event, scope : Scope
) { + ^space.emit(ID, ^event, scope) + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/InnerContextAccessSkill.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/InnerContextAccessSkill.sarl new file mode 100644 index 0000000000..ec1b69b6a3 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/InnerContextAccessSkill.sarl @@ -0,0 +1,191 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.skills.bic + +import io.sarl.core.InnerContextAccess +import io.sarl.lang.core.AgentContext +import io.sarl.lang.core.Event +import io.sarl.lang.core.Space +import io.sarl.lang.core.SpaceID +import io.sarl.lang.util.SynchronizedIterable +import io.sarl.sre.capacities.InternalEventBusCapacity +import io.sarl.sre.services.context.ContextService +import io.sarl.sre.services.lifecycle.AgentLife +import io.sarl.sre.skills.BuiltinSkill +import io.sarl.util.concurrent.Collections3 +import java.util.UUID +import java.util.concurrent.locks.ReadWriteLock +import javax.inject.Inject +import javax.inject.Provider + +import static io.sarl.sre.services.lifecycle.AgentLife.* + +/** + * SRE implementation of SARL's {@link InnerContextAccess} built-in capacity. + * + *

The inner context instance is stored into the {@link AgentLife}. + * + * @author $Author: srodriguez$ + * @author $Author: ngaud$ + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +@SuppressWarnings("potential_field_synchronization_problem") +skill InnerContextAccessSkill extends BuiltinSkill implements InnerContextAccess { + + uses InternalEventBusCapacity + + val contextService : ContextService + + /** Constructor. */ + @Inject + new (service : ContextService, lockProvider : Provider) { + super(lockProvider) + this.contextService = service + } + + /** Destroy the inner context instance. + * + *

This function is thread-safe. + */ + private def detroyInnerContext { + var context = getLife(owner, this.lockProvider).setInnerContext(null) + if (context !== null) { + var ^space = context.defaultSpace + // Unregister the agent from the default space + val listener = associatedEventBusListener + ^space.unregister(listener) + // Destroy the context + this.contextService.removeContext(context.ID) + } + } + + protected override uninstall(stage : UninstallationStage) { + if (stage === UninstallationStage.POST_DESTROY_EVENT) { + detroyInnerContext + } + } + + def getInnerContext : AgentContext { + val myself = owner + var newContext = false + var life = getLife(myself, this.lockProvider) + var context = life.innerContext + if (context === null) { + var lck = life.getLock + lck.writeLock.lock + try { + context = life.innerContext + if (context === null) { + var spaceId = life.addressInInnerDefaultSpace.spaceID + context = this.contextService.createContext(spaceId.contextID, spaceId.ID, myself) + life.innerContext = context + newContext = true + } + } finally { + lck.writeLock.unlock + } + } + if (newContext) { + // Register the agent in the default space + var ^space = context.defaultSpace + // Unregister the agent from the default space + val listener = associatedEventBusListener + ^space.register(listener) + } + return context + } + + def isInInnerDefaultSpace(^event : Event) : boolean { + if (^event !== null) { + val adr = ^event.source + if (adr !== null) { + return adr.spaceID.isInnerDefaultSpace + } + } + return false + } + + def isInnerDefaultSpace(^space : Space) : boolean { + isInnerDefaultSpace(^space.spaceID) + } + + def isInnerDefaultSpace(spaceID : SpaceID) : boolean { + val context = getLife(owner, this.lockProvider).innerContext + context !== null && spaceID == context.defaultSpace.spaceID + } + + def isInnerDefaultSpace(spaceID : UUID) : boolean { + val context = getLife(owner, this.lockProvider).innerContext + context !== null && spaceID == context.defaultSpace.spaceID.ID + } + + def getMemberAgentCount : int { + val context = getLife(owner, this.lockProvider).innerContext + if (context !== null) { + val participants = context.defaultSpace.participants + assert participants !== null + var count : int + val lck = participants.lock + lck.readLock.lock + try { + assert !context.rootContext, "An inner context cannot be a root context" + assert participants.contains(owner.ID), "The agent must be a participant of its inner conntext's default space" + count = participants.size - 1 + } finally { + lck.readLock.unlock + } + return count + } + return 0 + } + + def getMemberAgents : SynchronizedIterable { + val context = getLife(owner, this.lockProvider).innerContext + if (context !== null) { + val participants = context.defaultSpace.participants + assert participants !== null + var filtered = participants.filter [it != this.owner.ID] + return Collections3::unmodifiableSynchronizedIterable(filtered, participants.lock) + } + return Collections3::emptySynchronizedSet + } + + def hasMemberAgent : boolean { + val context = getLife(owner, this.lockProvider).innerContext + if (context !== null) { + val participants = context.defaultSpace.participants + assert participants !== null + val lck = participants.lock + lck.readLock.lock + try { + return (participants.size > 1) || + ((participants.size == 1) && (!participants.contains(this.owner.ID))) + } finally { + lck.readLock.unlock + } + } + return false; + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/LifecycleSkill.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/LifecycleSkill.sarl new file mode 100644 index 0000000000..8757969b1b --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/LifecycleSkill.sarl @@ -0,0 +1,173 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.skills.bic + +import io.sarl.core.Lifecycle +import io.sarl.core.Logging +import io.sarl.lang.core.Agent +import io.sarl.lang.core.AgentContext +import io.sarl.sre.services.context.Context +import io.sarl.sre.services.executor.ExecutorService +import io.sarl.sre.services.lifecycle.AgentState +import io.sarl.sre.services.lifecycle.LifecycleService +import io.sarl.sre.skills.BuiltinSkill +import java.util.UUID +import java.util.concurrent.locks.ReadWriteLock +import javax.inject.Inject +import javax.inject.Provider + +import static io.sarl.sre.services.lifecycle.AgentLife.* + +/** + * Skill that permits to manage the life cycle of the agents. + * + * @author $Author: srodriguez$ + * @author $Author: ngaud$ + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +@SuppressWarnings("potential_field_synchronization_problem") + skill LifecycleSkill extends BuiltinSkill implements Lifecycle { + + uses Logging + + val lifecycle : LifecycleService + + /** Constructor. + */ + @Inject + new (service : LifecycleService, provider : Provider) { + super(provider) + this.lifecycle = service + } + + @SuppressWarnings("discouraged_reference") + def killMe { + var postTreatment : Runnable = null + var state = getLife(owner, this.lockProvider).state + // The agent should be killed by a specific asynchronous event. + // This event is supported by the internal event bus implementation. + if (state == AgentState::INITIALIZING) { + // Indicate to the starting function into AgentLife that the initialization has failed. + postTreatment = [getLife(owner, this.lockProvider).state = AgentState::DYING] + } else if (state == AgentState::ALIVE) { + // Synchronous killing + postTreatment = [this.lifecycle.killAgent(this.owner)] + } + // Never return from the killMe + Thread::yield + ExecutorService::neverReturn(postTreatment) + } + + def spawn(agentType : Class, params : Object*) : UUID { + val life = getLife(owner, this.lockProvider) + if (life.state.alive) { + var o = this.owner + val result = this.lifecycle.spawnAgent(1, o.ID, life.defaultContext.context, null, agentType, params) + if (result.agentSpawned) { + var iterator = result.spawnedAgents.iterator + if (iterator.hasNext) { + return iterator.next + } + } else { + val err = result.error + if (err !== null) { + error(err.localizedMessage, err) + } + } + } + return null + } + + def spawn(nbAgents : int, agentType : Class, params : Object*) : Iterable { + val life = getLife(owner, this.lockProvider) + if (life.state.alive) { + var o = this.owner + var result = this.lifecycle.spawnAgent(nbAgents, o.ID, life.defaultContext.context, null, agentType, params) + if (result.failing) { + val err = result.error + if (err !== null) { + error(err.localizedMessage, err) + } + } + return result.spawnedAgents + } + return emptyList + } + + def spawnInContext(agentClass : Class, context : AgentContext, params : Object*) : UUID { + if (getLife(owner, this.lockProvider).state.alive) { + var o = this.owner + val result = this.lifecycle.spawnAgent(1, o.ID, context as Context, null, agentClass, params) + if (result.agentSpawned) { + var iterator = result.spawnedAgents.iterator + if (iterator.hasNext) { + return iterator.next + } + } else { + val err = result.error + if (err !== null) { + error(err.localizedMessage, err) + } + } + } + return null + } + + def spawnInContext(nbAgents : int, agentClass : Class, context : AgentContext, + params : Object*) : Iterable { + if (getLife(owner, this.lockProvider).state.alive) { + var o = this.owner + var result = this.lifecycle.spawnAgent(nbAgents, o.ID, context as Context, null, agentClass, params) + if (result.failing) { + val err = result.error + if (err !== null) { + error(err.localizedMessage, err) + } + } + return result.spawnedAgents + } + return emptyList + } + + def spawnInContextWithID(agentClass : Class, agentID : UUID, context : AgentContext, + params : Object*) : UUID { + if (getLife(owner, this.lockProvider).state.alive) { + var o = this.owner + val result = this.lifecycle.spawnAgent(1, o.ID, context as Context, agentID, agentClass, params) + if (result.agentSpawned) { + var iterator = result.spawnedAgents.iterator + if (iterator.hasNext) { + return iterator.next + } + } else { + val err = result.error + if (err !== null) { + error(err.localizedMessage, err) + } + } + } + return null + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/LoggingSkill.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/LoggingSkill.sarl new file mode 100644 index 0000000000..c208cb260a --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/LoggingSkill.sarl @@ -0,0 +1,233 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.skills.bic + +import com.google.common.base.Throwables +import io.sarl.core.Logging +import io.sarl.sre.services.logging.LoggingService +import io.sarl.sre.skills.BuiltinSkill +import java.text.MessageFormat +import java.util.concurrent.locks.ReadWriteLock +import java.util.function.Supplier +import java.util.logging.Level +import java.util.logging.LogRecord +import java.util.logging.Logger +import javax.inject.Inject +import javax.inject.Provider + +/** + * SRE implementation of SARL's {@link Logging} built-in capacity. + * + * @author $Author: srodriguez$ + * @author $Author: ngaud$ + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +@SuppressWarnings("potential_field_synchronization_problem") +skill LoggingSkill extends BuiltinSkill implements Logging { + + val logService : LoggingService + + var loggerInstance : Logger + + /** Constructor. + */ + @Inject + new (service : LoggingService, lockProvider : Provider) { + super(lockProvider) + this.logService = service + } + + + def getLogger : Logger { + if (this.loggerInstance === null) { + val agentId = owner.ID + val loggerName = MessageFormat::format(Messages::LoggingSkill_0, agentId) + this.loggerInstance = this.logService.createAgentLogger(loggerName) + } + this.loggerInstance + } + + def setLoggingName(name : String) { + var loggingName = name + if (loggingName.nullOrEmpty) { + loggingName = MessageFormat::format(Messages::LoggingSkill_0, owner.ID) + } + val level = this.logger.level + this.loggerInstance = this.logService.createAgentLogger(loggingName) + this.logger.level = level + } + + @Deprecated + def println(message : Object) { + info(message) + } + + def error(message : Object, exception : Throwable, parameters : Object*) { + if (this.logger.isLoggable(Level::SEVERE) && message !== null) { + val loggeableMessage = message.toString + if (!loggeableMessage.nullOrEmpty) { + if (exception !== null) { + val lr = new LogRecord(Level::SEVERE, loggeableMessage) + lr.parameters = parameters + lr.thrown = Throwables::getRootCause(exception) + this.logger.log(lr) + } else { + this.logger.log(Level::SEVERE, loggeableMessage, parameters) + } + } + } + } + + def error(messageProvider : Supplier) { + if (this.logger.isLoggable(Level::SEVERE) && messageProvider !== null) { + this.logger.log(Level::SEVERE, messageProvider) + } + } + + def warning(message : Object, exception : Throwable, parameters : Object*) { + if (this.logger.isLoggable(Level::WARNING) && message !== null) { + val loggeableMessage = message.toString + if (!loggeableMessage.nullOrEmpty) { + if (exception !== null) { + val lr = new LogRecord(Level::WARNING, loggeableMessage) + lr.parameters = parameters + lr.thrown = exception + this.logger.log(lr) + } else { + this.logger.log(Level::WARNING, loggeableMessage, parameters) + } + } + } + } + + def warning(messageProvider : Supplier) { + if (this.logger.isLoggable(Level::WARNING) && messageProvider !== null) { + this.logger.log(Level::WARNING, messageProvider) + } + } + + def info(message : Object, parameters : Object*) { + if (this.logger.isLoggable(Level::INFO) && message !== null) { + val loggeableMessage = message.toString + if (!loggeableMessage.nullOrEmpty) { + this.logger.log(Level::INFO, loggeableMessage, parameters) + } + } + } + + def info(messageProvider : Supplier) { + if (this.logger.isLoggable(Level::INFO) && messageProvider !== null) { + this.logger.log(Level::INFO, messageProvider) + } + } + + def debug(message : Object, parameters : Object*) { + if (this.logger.isLoggable(Level::CONFIG) && message !== null) { + val loggeableMessage = message.toString + if (!loggeableMessage.nullOrEmpty) { + this.logger.log(Level::CONFIG, loggeableMessage, parameters) + } + } + } + + def debug(messageProvider : Supplier) { + if (this.logger.isLoggable(Level::CONFIG) && messageProvider !== null) { + this.logger.log(Level::CONFIG, messageProvider) + } + } + + def isErrorLogEnabled : boolean { + this.logger.isLoggable(Level::SEVERE) + } + + def isWarningLogEnabled : boolean { + this.logger.isLoggable(Level::WARNING) + } + + def isInfoLogEnabled : boolean { + this.logger.isLoggable(Level::INFO) + } + + def isDebugLogEnabled : boolean { + this.logger.isLoggable(Level::CONFIG) + } + + def getLogLevel : int { + /* + * 0 or lower for no logging, + * 1 for error, + * 2 for warning, + * 3 for info, + * 4 or higher for debug. + */ + switch (this.logger.level) { + case Level::OFF: { + return 0 + } + case Level::SEVERE: { + return 1 + } + case Level::WARNING: { + return 2 + } + case Level::INFO: { + return 3 + } + default: { + return 4 + } + } + } + + def setLogLevel(level : int) { + /* + * 0 or lower for no logging, + * 1 for error, + * 2 for warning, + * 3 for info, + * 4 or higher for debug. + */ + var obj : Level + switch (level) { + case 1: { + obj = Level::SEVERE + } + case 2: { + obj = Level::WARNING + } + case 3: { + obj = Level::INFO + } + default: { + if (level <= 0) { + obj = Level::OFF + } else { + obj = Level::FINE + } + } + } + this.logger.level = obj + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/Messages.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/Messages.sarl new file mode 100644 index 0000000000..9b3d0d70c7 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/Messages.sarl @@ -0,0 +1,52 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.skills.bic + +import org.eclipse.osgi.util.NLS + +/** Messages. + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +final class Messages extends NLS { + + static val BUNDLE_NAME = typeof(Messages).getPackage.name + ".messages" + + static new { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, typeof(Messages)) + } + + public static var LoggingSkill_0 : String + + public static var SchedulesSkill_0 : String + public static var SchedulesSkill_1 : String + public static var SchedulesSkill_2 : String + + public static var ExternalContextAccessSkill_0 : String + + private new { + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/SchedulesSkill.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/SchedulesSkill.sarl new file mode 100644 index 0000000000..4502cd2a2f --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/SchedulesSkill.sarl @@ -0,0 +1,821 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.skills.bic + +import io.sarl.core.AgentTask +import io.sarl.core.Logging +import io.sarl.core.Time +import io.sarl.lang.annotation.PrivateAPI +import io.sarl.lang.core.Agent +import io.sarl.lang.core.Behavior +import io.sarl.lang.core.Capacities +import io.sarl.lang.core.SREutils +import io.sarl.lang.util.SynchronizedSet +import io.sarl.sre.capacities.InternalSchedules +import io.sarl.sre.services.executor.EarlyExitException +import io.sarl.sre.services.executor.ExecutorService +import io.sarl.sre.services.executor.SreRunnable +import io.sarl.sre.skills.BuiltinSkill +import io.sarl.util.concurrent.Collections3 +import java.lang.ref.WeakReference +import java.text.MessageFormat +import java.util.Map +import java.util.UUID +import java.util.concurrent.ExecutionException +import java.util.concurrent.Future +import java.util.concurrent.TimeUnit +import java.util.concurrent.TimeoutException +import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.locks.ReadWriteLock +import java.util.logging.Logger +import javax.inject.Inject +import javax.inject.Provider + +import static io.sarl.sre.services.lifecycle.AgentLife.* +import static io.sarl.sre.services.lifecycle.BehaviorLife.* + +/** + * Skill that permits to execute tasks with an executor service. + * + * @author $Author: srodriguez$ + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +@SuppressWarnings("use_reserved_sarl_annotation", "potential_field_synchronization_problem") +@PrivateAPI(isCallerOnly = true) +skill SchedulesSkill extends BuiltinSkill implements InternalSchedules { + + uses Logging, Time + + val executorService : ExecutorService + + var activeTaskRepository : Map = null + + val lock : ReadWriteLock + + /** Constructor. */ + @Inject + new (service : ExecutorService, lockProvider : Provider) { + super(lockProvider) + this.executorService = service + this.lock = lockProvider.get + } + + /** Replies the synchronization lock object. + */ + protected def getLock : ReadWriteLock { + this.lock + } + + protected override uninstall(stage : UninstallationStage) { + var lck = getLock + lck.writeLock.lock + try { + if (stage == UninstallationStage.PRE_DESTROY_EVENT) { + // Cancel the tasks as soon as possible in the uninstallation process + cancelAllRunningTasks + } else { + // Cancel the tasks that were creating during the destruction stage (in the Destroy event handler) + cancelAllRunningTasks + } + } finally { + lck.writeLock.unlock + } + } + + /** Replies the map that store the active tasks. + * If the map is not created before, this function creates it. + * + *

This function is not thread-safe. + */ + private def getRepository : Map { + if (this.activeTaskRepository === null) { + this.activeTaskRepository = newTreeMap(null) + } + this.activeTaskRepository + } + + /** Cancel all the running tasks. + * + *

This function is not thread-safe. + */ + private def cancelAllRunningTasks { + var activeTasks = this.activeTaskRepository + this.activeTaskRepository = null + if (activeTasks !== null) { + for (taskDescription : activeTasks.entrySet) { + var description = taskDescription.value + if (description !== null) { + var task = description.task + if (task !== null) { + finishTask(task, true, false, false) + } + } + } + } + } + + /** Create a task. + * If the task was already launched, this function replies the active task. + * + *

If {@code updateBehaviorReference} evaluates to {@code true} and the caller + * of the function's skill is a {@code Behavior}, then this latest behavior + * is associated to the task. + * + *

This function is thread-safe. + * + * @param name the name of the task to be created. + * @param updateTaskList is {@code true} for adding the created task into the list of active tasks. + * @param updateBehaviorReference indicates if the behavior reference to the task must be updated by removing the reference. + */ + private def createTaskIfNecessary(name : String = null, updateTaskList : boolean, + updateBehaviorReference : boolean) : TaskDescription { + var lck = getLock + var description : TaskDescription = null + var realName : String + if (name.isNullOrEmpty) { + realName = "task-" + UUID::randomUUID.toString + } else { + realName = name + lck.readLock.lock + try { + description = this.activeTaskRepository?.get(realName) + } finally { + lck.readLock.unlock + } + } + + if (description === null) { + val caller = Capacities::getCaller ?: this.owner + val task = new AgentTask(caller) + task.taskName = realName + task.guard = AgentTask::TRUE_GUARD + description = new TaskDescription(task) + SREutils.setSreSpecificData(task, description) + if (updateTaskList || updateBehaviorReference) { + lck.writeLock.lock + try { + if (updateBehaviorReference) { + getRepository.put(realName, description) + } + if (updateBehaviorReference && caller instanceof Behavior) { + // Register the task to the behavior in order to cancel the task when the behavior is unregistered. + task.bindToBehavior(caller) + } + } finally { + lck.writeLock.unlock + } + } + } + + return description + } + + private def unbindToBehavior(task : AgentTask) { + var caller = task.initiator + if (caller instanceof Behavior) { + getLife(caller, this.lockProvider).removeTaskReference(task) + } + } + + private def bindToBehavior(task : AgentTask, caller : Object = null) { + var initiator = caller ?: (task.initiator ?: (Capacities::getCaller ?: this.owner)) + if (initiator instanceof Behavior) { + getLife(initiator, this.lockProvider).addTaskReference(task) + } + } + + private def resetTask(newTask : AgentTask, description : TaskDescription) { + assert newTask !== null + assert description !== null + + var oldTask = description.task + if (oldTask !== null) { + assert oldTask.name == newTask.name + SREutils::setSreSpecificData(oldTask, null, typeof(TaskDescription)) + oldTask.unbindToBehavior + } + + SREutils::setSreSpecificData(newTask, description, typeof(TaskDescription)) + val name = newTask.name + if (name.isNullOrEmpty) { + newTask.taskName = "task-" + UUID::randomUUID.toString + } + description.task = newTask + description.future = null + newTask.bindToBehavior + } + + /** + * Remove any reference to the given task. + * The repository of active tasks is updated. + * + *

This function is not thread-safe. + * + * @param name the task. + * @param cancelTask indicates if the task should be canceled by this function. + * @param updateTaskList indicates if the task list must be updated by removing the task. + * @param updateBehaviorReference indicates if the behavior reference to the task must be updated by removing the reference. + * @return {@code true} if the task was successfully finished. + */ + private def finishTask(task : AgentTask, cancelTask : boolean, updateTaskList : boolean, updateBehaviorReference : boolean) : boolean { + assert task !== null + // Remove the reference of the behavior to the task. + if (updateBehaviorReference) { + task.unbindToBehavior + } + // Remove the task from the global list. + var description : TaskDescription = null + if (updateTaskList && !task.name.nullOrEmpty) { + description = this.activeTaskRepository?.remove(task.name) + } + if (cancelTask) { + // Force the stop of the task. + var description2 = SREutils::setSreSpecificData(task, null, typeof(TaskDescription)) + if (description === null) { + description = description2 + } + if (description !== null) { + var future = description.future + description.future = null + if (future !== null && !future.done && !future.cancelled) { + // Task is running. Force its stop. + return future.cancel(true) + } + } + } + return true; + } + + /** Replies the task description of the given task. + * + * @param task the task reference. + * @return the task description, or {@code null} if the task is unknown. + */ + protected def getTaskDescription(task : AgentTask) : TaskDescription { + if (task === null) { + return null; + } + var description = SREutils::getSreSpecificData(task, typeof(TaskDescription)) + if (description === null && !task.name.nullOrEmpty) { + description = this.activeTaskRepository?.get(task.name) + } + return description + } + + /** Prepare a task for submission. + * + *

This function creates the AgentTask and the task description if they are not created yet. + * This function put the task into the list of active tasks. + * + *

This function is thread-safe. + */ + private def preRunTask(task : AgentTask, procedure : (Agent)=>void) : TaskDescription { + var description : TaskDescription + var taskInstance : AgentTask + if (task === null) { + description = createTaskIfNecessary(true, true) + taskInstance = description.task + } else { + if (task.name.nullOrEmpty) { + description = null + } else { + var lck = getLock + lck.readLock.lock + try { + description = this.activeTaskRepository?.get(task.name) + } finally { + lck.readLock.unlock + } + } + var registered = description !== null + if (description === null) { + description = SREutils::getSreSpecificData(task, typeof(TaskDescription)) + } + if (description !== null) { + var future = description.future + if (future !== null && !future.done && !future.cancelled) { + throw new IllegalStateException(Messages::SchedulesSkill_2) + } + } else { + description = new TaskDescription() + } + task.resetTask(description) + if (!registered) { + var lck = getLock + lck.writeLock.lock + try { + getRepository.put(task.name, description) + } finally { + lck.writeLock.unlock + } + } + taskInstance = task + } + taskInstance.procedure = procedure + return description + } + + /** Finalize the task initialization. + * + *

This function is thread-safe. + */ + private def postRunTask(description : TaskDescription, task : AgentTask, future : Future) : TaskDescription { + assert description !== null + description.future = future + return description + } + + /** Called by a behavior when it is destroyed. */ + def unregisterTasksForBehavior(^behavior : Behavior) { + var tasksToCancel = getLife(^behavior, this.lockProvider).removeAllTaskReferences + if (tasksToCancel !== null) { + for (taskToCancel : tasksToCancel.map[it.get]) { + if (taskToCancel !== null) { + taskToCancel.finishTask(true, true, false) + } + } + } + } + + /** Called by a behavior when it is destroyed. + */ + def releaseInternalResources(^behavior : Behavior) { + SREutils::setSreSpecificData(^behavior, null) + } + + def task(name : String) : AgentTask { + if (getLife(owner, this.lockProvider).state.alive) { + return createTaskIfNecessary(name, false, false).task + } + return null + } + + def setName(task : AgentTask, name : String) { + if (getLife(owner, this.lockProvider).state.alive) { + val realName = if (name.isNullOrEmpty) "task-" + UUID::randomUUID.toString else name + val prefix = realName + "-" + var i = 0 + var nm = realName + var atr : Map + var lck = getLock + lck.readLock.lock + try { + atr = this.activeTaskRepository + } finally { + lck.readLock.unlock + } + if (atr !== null) { + lck.writeLock.lock + try { + atr = this.activeTaskRepository + if (atr !== null) { + var desc = atr.remove(task.name) + if (desc !== null) { + while (atr.containsKey(nm)) { + i++ + nm = prefix + i + } + task.taskName = nm + atr.put(nm, desc) + } + } else { + task.taskName = nm + } + } finally { + lck.writeLock.unlock + } + } else { + task.taskName = nm + } + } + } + + def execute(task : AgentTask = null, procedure : (Agent)=>void) : AgentTask { + var description = preRunTask(task, procedure) + var logger = getLogger + val future = this.executorService.executeAsap(logger, new SingleRunner(this, this.owner, description, logger)) + description = postRunTask(description, task, future) + return description.task + } + + def in(task : AgentTask = null, delay : long, procedure : (Agent)=>void) : AgentTask { + if (getLife(owner, this.lockProvider).state.alive) { + var description = preRunTask(task, procedure) + val logger = getLogger + val sf = this.executorService.schedule(logger, delay, TimeUnit::MILLISECONDS, + new SingleRunner(this, this.owner, description, logger)) + description = postRunTask(description, task, sf) + return description.task + } + return task + } + + def every(task : AgentTask = null, period : long, procedure : (Agent)=>void) : AgentTask { + if (getLife(owner, this.lockProvider).state.alive) { + var description = preRunTask(task, procedure) + val logger = getLogger + val sf = this.executorService.scheduleAtFixedRate(logger, 0, period, TimeUnit::MILLISECONDS, + new PeriodicRunner(this, this.owner, description, logger)) + description = postRunTask(description, task, sf) + return description.task + } + return task + } + + def atFixedDelay(task : AgentTask = null, delay : long, procedure : (Agent)=>void) : AgentTask { + if (getLife(owner, this.lockProvider).state.alive) { + var description = preRunTask(task, procedure) + var future : Future + if (delay <= 0) { + future = this.executorService.executeAsap(new InfiniteRunner(this, this.owner, description, getLogger)) + } else { + var logger = getLogger + future = this.executorService.scheduleWithFixedDelay(logger, 0, delay, TimeUnit::MILLISECONDS, + new PeriodicRunner(this, this.owner, description, logger)) + } + description = postRunTask(description, task, future) + return description.task + } + return task + } + + def at(task : AgentTask = null, time : long, procedure : (Agent)=>void) : AgentTask { + val delay = Math::round(time - getTime) + if (delay > 0.0) { + return in(task, delay, procedure) + } + return task + } + + def isCanceled(task : AgentTask) : boolean { + if (task !== null) { + var description = task.taskDescription + if (description !== null) { + var future = description.future + if (future !== null) { + return future.cancelled + } + } + } + return false + } + + def cancel(task : AgentTask, mayInterruptIfRunning : boolean = true) : boolean { + if(task !== null) { + var description = task.taskDescription + if (description !== null) { + var future = description.future + if (future !== null && !future.done && !future.cancelled + && future.cancel(mayInterruptIfRunning)) { + return finishTask(task, true, true, true) + } + } + } + return false + } + + def getActiveTasks : SynchronizedSet { + var lck = getLock + lck.readLock.lock + try { + if (this.activeTaskRepository !== null) { + return Collections3::unmodifiableSynchronizedSet(this.activeTaskRepository.keySet, lck) + } + return Collections3::emptySynchronizedSet + } finally { + lck.readLock.unlock + } + } + + /** + * Description of a task. + * + * @author $Author: sgalland$ + * @version $Name$ $Revision$ $Date$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.5.0 + */ + private static final class TaskDescription { + + var taskReference : AgentTask + + /** The scheduled future associated to the task. + */ + var futureResult : Future + + new (task : AgentTask = null, future : Future = null) { + this.taskReference = task + if (future === null) { + this.futureResult = new FutureReceiver + } else { + this.futureResult = future + } + } + + def toString : String { + this.taskReference?.toString + } + + def getTask : AgentTask { + this.taskReference + } + + package def setTask(task : AgentTask) { + this.taskReference = task + } + + def getFuture : Future { + this.futureResult + } + + package def setFuture(future : Future) { + var receiver : FutureReceiver + if (this.futureResult instanceof FutureReceiver) { + receiver = this.futureResult + } else { + receiver = null + } + this.futureResult = future + if (receiver !== null && this.futureResult !== null) { + receiver.apply(this.futureResult) + } + } + + } + + /** + * A future definition that enables to interact with the future + * object's even if it is not already provided by the thread manager. + * This receiver will be replaced by the real future object as soon + * as it is provided by the thread manager. Then, any interaction with + * the receiver will be propagated to the real future. + * + * @author $Author: sgalland$ + * @version $Name$ $Revision$ $Date$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.9 + */ + private static class FutureReceiver implements Future { + + val cancelFlag = new AtomicBoolean + + val mayInterruptIfRunningFlag = new AtomicBoolean + + package new { + // + } + + def apply(future : Future) { + if (future !== null && !future.isCancelled && !future.isDone && this.cancelFlag.get) { + future.cancel(this.mayInterruptIfRunningFlag.get) + } + } + + override cancel(mayInterruptIfRunning : boolean) : boolean { + this.mayInterruptIfRunningFlag.set(mayInterruptIfRunning) + this.cancelFlag.set(true) + return true + } + + override isCancelled : boolean { + this.cancelFlag.get + } + + override isDone : boolean { + false + } + + override get : Object throws InterruptedException, ExecutionException { + throw new ExecutionException(new UnsupportedOperationException) + } + + override get(timeout : long, unit : TimeUnit) : Object + throws InterruptedException, ExecutionException, TimeoutException { + throw new ExecutionException(new UnsupportedOperationException) + } + + } + + /** + * Type of task termination. + * + * @author $Author: sgalland$ + * @version $Name$ $Revision$ $Date$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.6.0 + */ + private enum TaskTermination { + PROCEDURE_RUN, NO_PROCEDURE_RUN, ERROR_IN_PROCEDURE + } + + /** + * Implementation of an agent task that is run once time. + * + * @author $Author: sgalland$ + * @version $Name$ $Revision$ $Date$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ + private static abstract class TaskRunner extends SreRunnable { + + protected val ^skill : WeakReference + + protected val ^agent : WeakReference + + protected val agentTaskRef : WeakReference + + new (^skill : SchedulesSkill, ^agent : Agent, task : TaskDescription, logger : Logger) { + super(logger) + assert ^skill !== null + assert ^agent !== null + assert task !== null + this.^skill = new WeakReference(^skill) + this.^agent = new WeakReference(^agent) + this.agentTaskRef = new WeakReference(task) + } + + protected def coreRun(task : AgentTask) : TaskTermination { + val owner = ^agent?.get + assert owner !== null + val guard = task.guard + if (guard === null || guard.apply(owner).booleanValue) { + val procedure = task.procedure + if (procedure !== null) { + procedure.apply(owner) + return TaskTermination::PROCEDURE_RUN + } + } + return TaskTermination::NO_PROCEDURE_RUN + } + + final override run { + val taskDescription = this.agentTaskRef.get + if (taskDescription === null) { + throw new RuntimeException(Messages.SchedulesSkill_0) + } + val task = taskDescription.task + if (task === null) { + throw new RuntimeException(Messages.SchedulesSkill_0) + } + val future = taskDescription.future + if (future !== null && (future.done || future.cancelled)) { + return + } + if (!preRun) { + return + } + val ^skill = this.^skill?.get + assert ^skill !== null + var mustBeFinished = TaskTermination::PROCEDURE_RUN + try { + try { + mustBeFinished = task.coreRun + } catch (ex : EarlyExitException) { + throw ex + } catch (ex : InterruptedException) { + throw ex + } catch (ex : Throwable) { + mustBeFinished = TaskTermination::ERROR_IN_PROCEDURE + this.logger.severe( + MessageFormat::format(Messages.SchedulesSkill_1, ex, toString, ex.getLocalizedMessage)) + } finally { + val finishTask = postRun(mustBeFinished) + if (finishTask) { + var lck = ^skill.getLock + lck.writeLock.lock + try { + ^skill.finishTask(task, false, true, true); + } finally { + lck.writeLock.unlock + } + } + } + } catch (ex : EarlyExitException) { + ex.runPostTreatment(this.logger) + } catch (ex : InterruptedException) { + // Ignore this exception + } + } + + protected def preRun : boolean { + true + } + + protected abstract def postRun(termination : TaskTermination) : boolean + + } + + /** + * Implementation of an agent task that is run once time. + * + * @author $Author: sgalland$ + * @version $Name$ $Revision$ $Date$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ + private static class SingleRunner extends TaskRunner { + + new (^skill : SchedulesSkill, ^agent : Agent, task : TaskDescription, logger : Logger) { + super(^skill, ^agent, task, logger) + } + + protected def postRun(termination : TaskTermination) : boolean { + true + } + + } + + /** + * Implementation of a periodic agent task. + * + * @author $Author: sgalland$ + * @version $Name$ $Revision$ $Date$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ + private static class PeriodicRunner extends TaskRunner { + + new (^skill : SchedulesSkill, ^agent : Agent, task : TaskDescription, logger : Logger) { + super(^skill, ^agent, task, logger) + } + + protected def postRun(termination : TaskTermination) : boolean { + termination !== TaskTermination::PROCEDURE_RUN + } + + } + + /** + * Implementation of an agent infinite loop task. + * + * @author $Author: sgalland$ + * @version $Name$ $Revision$ $Date$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ + private static class InfiniteRunner extends TaskRunner { + + new (^skill : SchedulesSkill, ^agent : Agent, task : TaskDescription, logger : Logger) { + super(^skill, ^agent, task, logger) + } + + private def canRun : boolean { + var taskDescription = this.agentTaskRef.get + if (taskDescription !== null) { + var future = taskDescription.future + return future !== null && !future.done && !future.cancelled + } + return false + } + + protected def postRun(termination : TaskTermination) : boolean { + true + } + + @SuppressWarnings("discouraged_reference") + protected override coreRun(task : AgentTask) : TaskTermination { + var owner = ^agent.get + assert owner !== null + while (canRun) { + val guard = task.guard + if (guard === null || guard.apply(owner).booleanValue) { + val procedure = task.procedure + if (procedure !== null) { + procedure.apply(owner) + } + } else { + // Break the loop without introducing a local boolean variable + break + } + Thread::yield + } + return TaskTermination::NO_PROCEDURE_RUN + } + + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/TimeSkill.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/TimeSkill.sarl new file mode 100644 index 0000000000..c8284b2822 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/TimeSkill.sarl @@ -0,0 +1,75 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.skills.bic + +import io.sarl.core.Time +import io.sarl.sre.services.time.TimeService +import io.sarl.sre.skills.BuiltinSkill +import java.util.concurrent.TimeUnit +import java.util.concurrent.locks.ReadWriteLock +import javax.inject.Inject +import javax.inject.Provider + +/** + * SRE implementation of SARL's {@link Time} built-in capacity. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +@SuppressWarnings("potential_field_synchronization_problem") +skill TimeSkill extends BuiltinSkill implements Time { + + val timeService : TimeService + + /** Constructor. */ + @Inject + new (service : TimeService, provider : Provider) { + super(provider) + this.timeService = service + } + + def getTime(timeUnit : TimeUnit) : double { + this.timeService.getTime(timeUnit) + } + + def getOSTimeFactor : double { + this.timeService.OSTimeFactor + } + + def fromOSDuration(timeDuration : double) : double { + this.timeService.fromOSDuration(timeDuration) + } + + def fromOSTime(timeValue : double) : double { + this.timeService.fromOSTime(timeValue) + } + + def toOSDuration(timeDuration : double) : double { + this.timeService.toOSDuration(timeDuration) + } + + def toOSTime(timeValue : double) : double { + this.timeService.toOSTime(timeValue) + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/messages.properties b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/messages.properties new file mode 100644 index 0000000000..149c8873ce --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/bic/messages.properties @@ -0,0 +1,5 @@ +LoggingSkill_0=AGENT-{0} +SchedulesSkill_0=Agent task is null. +SchedulesSkill_1=Error in agent''s task {0}: {1} +SchedulesSkill_2=Already launched task. +ExternalContextAccessSkill_0=The context ID ''{0}'' is known by the agent diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/internal/EventBus.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/internal/EventBus.sarl new file mode 100644 index 0000000000..8526177ece --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/internal/EventBus.sarl @@ -0,0 +1,347 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.skills.internal + +import io.sarl.lang.core.Event +import io.sarl.lang.util.SynchronizedIterable +import io.sarl.revision.BehaviorGuardEvaluator +import io.sarl.revision.BehaviorGuardEvaluatorRegistry +import io.sarl.sre.services.executor.ExecutorService +import io.sarl.util.concurrent.Collections3 +import java.util.Collection +import java.util.concurrent.locks.ReadWriteLock +import java.util.logging.Logger +import javax.inject.Inject +import javax.inject.Provider +import org.arakhne.afc.util.MultiCollection + +/** + * The class in charge of dispatching every single events coming from the outside of this agent (i.e. from a space) or from an + * agent's behavior. + * + * @author $Author: ngaud$ + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * + */ +class EventBus { + + /** + * The registry of all {@code BehaviorGuardEvaluator} classes containing a method to evaluate the guard of a given behavior + * ("on" clause in SARL behavior). This class has been inspired by the com.google.common.eventbus.SuscriberRegistry class of + * Google Guava library. + */ + val behaviorGuardEvaluatorRegistry : BehaviorGuardEvaluatorRegistry + + val lock : ReadWriteLock + + /** + * The executor used to execute behavior methods in dedicated thread. + */ + val executor : ExecutorService + + /** + * Instantiates a dispatcher. + * + * @param executor the executor service. + * @param provider of synchronization locks. + */ + @Inject + new (executor : ExecutorService, lockProvider : Provider) { + this(executor, lockProvider, new BehaviorGuardEvaluatorRegistry) + } + + /** + * Instantiates a dispatcher. + * + * @param executor the executor service. + * @param provider of synchronization locks. + * @param dispatcher the event dispatcher. + */ + new (executor : ExecutorService, lockProvider : Provider, dispatcher : BehaviorGuardEvaluatorRegistry) { + assert executor !== null + assert lockProvider !== null + assert dispatcher !== null + this.executor = executor + this.lock = lockProvider.get + this.behaviorGuardEvaluatorRegistry = dispatcher + } + + /** Replies the lock. */ + protected def getLock : ReadWriteLock { + this.lock + } + + /** Replies if a listener with the given type is registered. + * + * @param type the type of listener. + * @return {@code true} if a listener of the given type is registered. + * @since 0.5.0 + */ + def hasRegisteredEventListener(type : Class) : boolean { + var lck = getLock + lck.readLock.lock + try { + this.behaviorGuardEvaluatorRegistry.hasRegisteredEventListener(type) + } finally { + lck.readLock.unlock + } + } + + /** Extract the registered listeners with the given type. + * + * @param the type of the listeners. + * @param type the type of the listeners. + * @param collection the collection of listeners that is filled by this function. + * @return the number of listeners added to the collection. + * @since 0.6.0 + */ + def getRegisteredEventListeners(type : Class) : SynchronizedIterable with T { + var lck = getLock + lck.readLock.lock + try { + Collections3::unmodifiableSynchronizedIterable( + this.behaviorGuardEvaluatorRegistry.getRegisteredEventListeners(type), + lck) + } finally { + lck.readLock.unlock + } + } + + /** + * Registers all {@code PerceptGuardEvaluator} methods on {@code object} to receive events. + * + *

If the filter is provided, it will be used for determining if the given behavior accepts a specific event. + * If the filter function replies {@code true} for a specific event as argument, the event is fired in the + * behavior context. If the filter function replies {@code false}, the event is not fired in the behavior context. + * + * @param object object whose {@code PerceptGuardEvaluator} methods should be registered. + * @param filter the filter function. It could be {@code null}. + * @param callback function which is invoked just after the first registration of the object. It could be {@code null}. + */ + def register(object : Object, filter : (Event)=>boolean, callback : (Object)=>void) { + var lck = getLock + lck.writeLock.lock + try { + this.behaviorGuardEvaluatorRegistry.register(object, filter, callback) + } finally { + lck.writeLock.unlock + } + } + + /** + * Unregisters all {@code PerceptGuardEvaluator} methods on a registered {@code object}. + * + * @param object object whose {@code PerceptGuardEvaluator} methods should be unregistered. + * @param callback function which is invoked just before the object is unregistered. + * @throws IllegalArgumentException if the object was not previously registered. + */ + def unregister(object : Object, callback : (Object)=>void) { + var lck = getLock + lck.writeLock.lock + try { + this.behaviorGuardEvaluatorRegistry.unregister(object, callback) + } finally { + lck.writeLock.unlock + } + } + + /** + * Unregisters all {@code PerceptGuardEvaluator} methods on the objects of the given type. + * + * @param type type of the objects whose {@code PerceptGuardEvaluator} methods should be unregistered. + * @param callback function which is invoked just before the object is unregistered. + * @throws IllegalArgumentException if the object was not previously registered. + * @since 0.7.0 + */ + def unregister(type : Class, callback : (Object)=>void) { + var lck = getLock + lck.writeLock.lock + try { + this.behaviorGuardEvaluatorRegistry.unregister(type, callback) + } finally { + lck.writeLock.unlock + } + } + + /** + * Unregisters all {@code PerceptGuardEvaluator} methods on all registered objects. + * + * @param callback function which is invoked just before the object is unregistered. + * @throws IllegalArgumentException if the object was not previously registered. + */ + def unregisterAll(callback : (Object)=>void) { + var lck = getLock + lck.writeLock.lock + try { + this.behaviorGuardEvaluatorRegistry.unregisterAll(callback) + } finally { + lck.writeLock.unlock + } + } + + /** + * Posts an event to all registered {@code BehaviorGuardEvaluator}. + * The dispatch of the events within the agent is asynchronous. + * A rendez-vous point is set up in order to wait for all the event handlers to be finished. + * This method will return successfully after the event has been posted to all {@code BehaviorGuardEvaluator}, and regardless + * of any exceptions thrown by {@code BehaviorGuardEvaluator}. + * + * @param event an event to dispatch synchronously. + * @param thrownExceptions indicates if the exceptions in the event handlers should be thrown from this function, + * or logged out to the agent's log. + * @param logger the logger to use for notifying the errors. + */ + def immediateDispatch(^event : Event, thrownExceptions : boolean, logger : Logger = null) { + assert ^event !== null + var behaviorGuardEvaluators : Iterable = null + var lck = getLock + lck.readLock.lock + try { + behaviorGuardEvaluators = this.behaviorGuardEvaluatorRegistry.getBehaviorGuardEvaluators(^event) + } finally { + lck.readLock.unlock + } + if (behaviorGuardEvaluators !== null) { + var behaviorsMethodsToExecute = ^event.evaluateGuards(behaviorGuardEvaluators, logger) + if (!behaviorsMethodsToExecute.empty) { + behaviorsMethodsToExecute.executeBehaviorMethodsInParalellWithSynchroAtTheEnd(thrownExceptions, logger) + } + } + } + + /** + * Posts an event to the registered {@code BehaviorGuardEvaluator} of the given listener only. + * The dispatch of this event will be done synchronously. + * This method will return successfully after the event has been posted to all {@code BehaviorGuardEvaluator}, and regardless + * of any exceptions thrown by {@code BehaviorGuardEvaluator}. + * + * @param listener the listener to dispatch to. + * @param event an event to dispatch synchronously. + * @param thrownExceptions indicates if the exceptions in the event handlers should be thrown from this function, + * or logged out to the agent's log. + * @param logger the logger to use for notifying the errors. + */ + def immediateDispatchTo(listener : Object, ^event : Event, thrownExceptions : boolean, logger : Logger = null) { + assert ^event !== null + var behaviorGuardEvaluators : Iterable = null + var lck = getLock + lck.readLock.lock + try { + behaviorGuardEvaluators = this.behaviorGuardEvaluatorRegistry.getBehaviorGuardEvaluatorsFor(^event, listener) + } finally { + lck.readLock.unlock + } + if (behaviorGuardEvaluators !== null) { + var behaviorsMethodsToExecute = ^event.evaluateGuards(behaviorGuardEvaluators, logger) + behaviorsMethodsToExecute.executeBehaviorMethodsInParalellWithSynchroAtTheEnd(thrownExceptions, logger) + } + } + + /** + * Posts an event to all registered {@code BehaviorGuardEvaluator}. + * The dispatch of this event will be done asynchronously. + * This method will return successfully after the event has been posted to all {@code BehaviorGuardEvaluator}, and regardless + * of any exceptions thrown by {@code BehaviorGuardEvaluator}. + * + * @param event an event to dispatch asynchronously. + * @param logger the logger to use for notifying the errors. + */ + def asyncDispatch(^event : Event, logger : Logger = null) { + assert ^event !== null + var behaviorGuardEvaluators : Iterable = null + var lck = getLock + lck.readLock.lock + try { + behaviorGuardEvaluators = this.behaviorGuardEvaluatorRegistry.getBehaviorGuardEvaluators(^event) + } finally { + lck.readLock.unlock + } + if (behaviorGuardEvaluators !== null) { + var behaviorsMethodsToExecute = ^event.evaluateGuards(behaviorGuardEvaluators, logger) + if (!behaviorsMethodsToExecute.empty) { + behaviorsMethodsToExecute.executeAsynchronouslyBehaviorMethods(logger) + } + } + } + + /** + * Evaluate the guard associated to the specified {@code event} and returns the list of behaviors methods that must be + * executed. + * + *

Errors are logger by the executor service. But they are not stopping the call to this function. + * + * @param event the event triggering behaviors. + * @param behaviorGuardEvaluators the list of class containing a {@code PerceptGuardEvaluator} method. + * @param logger the logger to be used. + * @return the collection of couple associating a object and its collection of behavior methods that must be executed. + * @throws InvocationTargetException - exception when you try to execute a method by reflection and this method doesn't exist. + */ + protected def evaluateGuards(^event : Event, behaviorGuardEvaluators : Iterable, + logger : Logger) : Collection { + val behaviorsMethodsToExecute = new MultiCollection + this.executor.applyBlockingConsumer(logger, behaviorGuardEvaluators) [evaluator | + val behaviorsMethodsToExecutePerTarget = newLinkedList + evaluator.evaluateGuard(^event, behaviorsMethodsToExecutePerTarget) + synchronized (behaviorsMethodsToExecute) { + behaviorsMethodsToExecute.addCollection(behaviorsMethodsToExecutePerTarget) + } + ] + return behaviorsMethodsToExecute + } + + /** + * Execute every single Behaviors runnable, a dedicated thread will created by the executor local to this class and be used to + * execute each runnable in parallel, and this method waits until its future has been completed before leaving. + * + *

Errors are logged by the executor service, and are thrown by this function. + * + * @param behaviorsMethodsToExecute the collection of Behaviors runnable that must be executed. + * @param thrownExceptions indicates if the exceptions in the event handlers should be thrown from this function, + * or logged out to the agent's log. + * @param logger the logger to use for notifying the errors. + * @throws InterruptedException - something interrupt the waiting of the event handler terminations. + * @throws ExecutionException - when the event handlers cannot be called; or when one of the event handler has failed during + * its run. + */ + protected def executeBehaviorMethodsInParalellWithSynchroAtTheEnd(behaviorsMethodsToExecute : Collection, + thrownExceptions : boolean, logger : Logger) { + this.executor.executeBlockingTasks(logger, thrownExceptions, behaviorsMethodsToExecute) + } + + /** + * Execute every single Behaviors runnable, a dedicated thread will created by the executor local to this class and be used to + * execute each runnable in parallel. + * + *

Errors are logged by the executor service. They are not thrown by this function. + * + * @param logger the logger to use for notifying the errors. + * @param behaviorsMethodsToExecute the collection of Behaviors runnable that must be executed. + */ + protected def executeAsynchronouslyBehaviorMethods(behaviorsMethodsToExecute : Collection, logger : Logger) { + for (runnable : behaviorsMethodsToExecute) { + this.executor.executeAsap(logger, runnable) + } + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/internal/InternalEventBusSkill.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/internal/InternalEventBusSkill.sarl new file mode 100644 index 0000000000..669ab24257 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/internal/InternalEventBusSkill.sarl @@ -0,0 +1,336 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.skills.internal + +import io.sarl.core.Logging +import io.sarl.lang.core.Agent +import io.sarl.lang.core.Event +import io.sarl.lang.util.SynchronizedIterable +import io.sarl.sre.capacities.InformedEventListener +import io.sarl.sre.capacities.InternalEventBusCapacity +import io.sarl.sre.skills.BuiltinSkill +import io.sarl.util.concurrent.Collections3 +import java.lang.ref.WeakReference +import java.util.LinkedList +import java.util.List +import java.util.UUID +import java.util.concurrent.locks.ReadWriteLock +import javax.inject.Inject +import javax.inject.Provider + +import static io.sarl.sre.services.lifecycle.AgentLife.* + +/** + * SRE implementation of an internal skill that provides an event dispatcher to notify the different components/behaviors of an + * agent. + * + * @author $Author: srodriguez$ + * @author $Author: ngaud$ + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +@SuppressWarnings("potential_field_synchronization_problem") +skill InternalEventBusSkill extends BuiltinSkill implements InternalEventBusCapacity { + + uses Logging + + val eventBus : EventBus + + var eventListener : InformedEventListener + + var isEventBufferEnabled = false + + val lock : ReadWriteLock + + var eventBuffer : List = null + + @Inject + new (bus : EventBus, provider : Provider) { + super(provider) + this.eventBus = bus + this.lock = provider.get + } + + /** Replies the lock object. + */ + def getLock : ReadWriteLock { + this.lock + } + + override getEventBus : EventBus { + this.eventBus + } + + override getEventBus(type : Class) : T with T extends EventBus { + type.cast(this.eventBus) + } + + protected override install { + // Register the agent as a direct event listener. + var bus = this.eventBus + if (bus !== null) { + bus.register(this.owner, null, null) + } + } + + protected override uninstall(stage : UninstallationStage) { + if (stage == UninstallationStage::POST_DESTROY_EVENT) { + var bus = this.eventBus + if (bus !== null) { + bus.unregisterAll(null) + } + this.eventListener = null + this.eventBuffer = null; + } + } + + final def getAssociatedEventBusListener : InformedEventListener { + var lst : InformedEventListener + var lck = getLock + lck.readLock.lock + try { + lst = this.eventListener + } finally { + lck.readLock.unlock + } + if (lst === null) { + lck.writeLock.lock + try { + lst = this.eventListener + if (lst === null) { + lst = new ExternalEventBusAccessor(owner.ID, this) + this.eventListener = lst + } + } finally { + lck.writeLock.unlock + } + } + return lst + } + + def registerEventBusListener(listener : Object, filter : (Event)=>boolean, callback : (Object)=>void) { + this.eventBus.register(listener, filter, callback); + } + + def unregisterEventBusListener(listener : Object, callback : (Object)=>void = null) { + this.eventBus.unregister(listener, callback) + } + + def unregisterEventBusListener(listenerType : Class, callback : (Object)=>void = null) { + this.eventBus.unregister(listenerType, callback) + } + + /** Change the event buffering flag. + * + * @param buffering the value of the flag. + * @return the value of the flag before its change. + */ + def setEventBuffering(buffering : boolean) : boolean { + val lck = getLock + lck.writeLock.lock + try { + val old = this.isEventBufferEnabled + this.isEventBufferEnabled = buffering + return old + } finally { + lck.writeLock.unlock + } + } + + /** Replies if the events are buffering. + * + * @return {@code true} if the events are buffered. + */ + def isEventBuffering() : boolean { + val lck = getLock + lck.readLock.lock + try { + return this.isEventBufferEnabled + } finally { + lck.readLock.unlock + } + } + + /** Replies the buffered events. + * + * @return an unmodifiable iterable of the buffered elements. + */ + def getBufferedEvents : SynchronizedIterable { + val lck = getLock + lck.readLock.lock + try { + if (this.eventBuffer === null) { + return Collections3::emptySynchronizedSet + } + return Collections3::unmodifiableSynchronizedIterable(this.eventBuffer, lck) + } finally { + lck.readLock.unlock + } + } + + def fireEventAndWait(^event : Event, gatherEvents : boolean, thrownExceptions : boolean, + listener : Object) : Iterable { + if (getLife(owner, this.lockProvider).state.blockingEventHandling) { + if (gatherEvents) { + var wasEnabled : boolean + val lck = getLock + lck.writeLock.lock + try { + wasEnabled = this.isEventBufferEnabled + this.isEventBufferEnabled = true + } finally { + lck.writeLock.unlock + } + try { + this.eventBus.immediateDispatchTo(listener, ^event, thrownExceptions, getLogger) + } finally { + lck.writeLock.lock + try { + this.isEventBufferEnabled = wasEnabled + } finally { + lck.writeLock.unlock + } + } + lck.writeLock.lock + try { + var cache = this.eventBuffer + this.eventBuffer = null + return cache + } finally { + lck.writeLock.unlock + } + } + this.eventBus.immediateDispatchTo(listener, ^event, thrownExceptions, getLogger) + } + return emptyList + } + + def fireEventAndWait(^event : Event, gatherEvents : boolean, thrownExceptions : boolean) : Iterable { + if (getLife(owner, this.lockProvider).state.blockingEventHandling) { + if (gatherEvents) { + var wasEnabled : boolean + val lck = getLock + lck.writeLock.lock + try { + wasEnabled = this.isEventBufferEnabled + this.isEventBufferEnabled = true + } finally { + lck.writeLock.unlock + } + try { + this.eventBus.immediateDispatch(^event, thrownExceptions, getLogger) + } finally { + lck.writeLock.lock + try { + this.isEventBufferEnabled = wasEnabled + } finally { + lck.writeLock.unlock + } + } + lck.writeLock.lock + try { + var cache = this.eventBuffer + this.eventBuffer = null + return cache + } finally { + lck.writeLock.unlock + } + } + this.eventBus.immediateDispatch(^event, thrownExceptions, getLogger) + } + return emptyList + } + + + def fireEvent(^event : Event) { + if (getLife(owner, this.lockProvider).state.asynchronousEventHandling) { + var mustBuf : boolean + val lck = getLock + lck.readLock.lock + try { + mustBuf = this.isEventBufferEnabled + } finally { + lck.readLock.unlock + } + if (mustBuf) { + lck.writeLock.lock + try { + mustBuf = this.isEventBufferEnabled + if (mustBuf) { + if (this.eventBuffer === null) { + this.eventBuffer = new LinkedList + } + this.eventBuffer += ^event + } + } finally { + lck.writeLock.unlock + } + } else { + this.eventBus.asyncDispatch(^event, getLogger) + } + } + } + + def getRegisteredEventBusListeners(type : Class) : SynchronizedIterable with T { + this.eventBus.getRegisteredEventListeners(type) + } + + /** + * The class in charge of dispatching every single events coming from the outside of this agent (i.e. from a space) or from an + * agent's behavior. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * + */ + private static class ExternalEventBusAccessor implements InformedEventListener { + + val owner : WeakReference + + val id : UUID + + new (id : UUID, owner : InternalEventBusSkill) { + this.id = id + this.owner = new WeakReference(owner) + } + + private def getOwnerSkill : InternalEventBusSkill { + this.owner?.get + } + + override receiveEvent(^event : Event) { + this.ownerSkill?.fireEvent(^event) + } + + override getID : UUID { + this.id + } + + override getOwnerInstance : Agent { + this.ownerSkill?.owner + } + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/internal/MicroKernelSkill.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/internal/MicroKernelSkill.sarl new file mode 100644 index 0000000000..a83222c4c0 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/skills/internal/MicroKernelSkill.sarl @@ -0,0 +1,80 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.skills.internal + +import com.google.common.util.concurrent.Service +import io.sarl.sre.Kernel +import io.sarl.sre.capacities.MicroKernelCapacity +import io.sarl.sre.skills.BuiltinSkill +import java.lang.ref.WeakReference +import java.util.concurrent.locks.ReadWriteLock +import javax.inject.Inject +import javax.inject.Provider + +/** + * Capacity that provides an access to the micro kernel. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +@SuppressWarnings("potential_field_synchronization_problem") +skill MicroKernelSkill extends BuiltinSkill implements MicroKernelCapacity { + + var kernel : WeakReference + + @Inject + new (kernel : Kernel, provider : Provider) { + super(provider) + this.kernel = new WeakReference(kernel) + } + + protected override uninstall(stage : UninstallationStage) { + if (stage == UninstallationStage.POST_DESTROY_EVENT) { + val kernelReference = this.kernel + this.kernel = null + if (kernelReference !== null) { + kernelReference.clear + } + } + } + + /** + * Replies the kernel. + * + * @return the kernel, or null. + */ + protected def getKernel : Kernel { + this.kernel?.get + } + + def getService(type : Class) : S with S extends Service { + if (type !== null) { + val kern = getKernel + if (kern !== null) { + return kern.getService(type) + } + } + return null + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/AbstractEventSpace.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/AbstractEventSpace.sarl new file mode 100644 index 0000000000..68c2d561b0 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/AbstractEventSpace.sarl @@ -0,0 +1,267 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.spaces + +import io.sarl.lang.annotation.PrivateAPI +import io.sarl.lang.core.Address +import io.sarl.lang.core.Event +import io.sarl.lang.core.EventListener +import io.sarl.lang.core.EventSpace +import io.sarl.lang.core.SREutils +import io.sarl.lang.core.Scope +import io.sarl.lang.core.SpaceID +import io.sarl.lang.util.SynchronizedSet +import io.sarl.sre.services.logging.LoggingService +import io.sarl.util.AddressScope +import io.sarl.util.Scopes +import io.sarl.util.concurrent.Collections3 +import java.text.MessageFormat +import java.util.UUID +import java.util.concurrent.locks.ReadWriteLock +import java.util.logging.Level +import java.util.stream.Stream +import javax.inject.Inject +import javax.inject.Provider +import org.eclipse.xtend.lib.annotations.Accessors + +/** + * Abstract implementation of an event space. + * + * @author $Author: srodriguez$ + * @author $Author: ngaud$ + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +@SuppressWarnings("use_reserved_sarl_annotation") +@PrivateAPI(isCallerOnly = true) +abstract class AbstractEventSpace extends AbstractSpace implements EventSpace, SpaceWithParticipants { + + @Accessors(PUBLIC_GETTER) + val lock : ReadWriteLock + + /** + * Logging service. + */ + @Accessors(PUBLIC_GETTER) + val logger : LoggingService + + /** Router of the events. + * @since 0.8 + */ + @Accessors(PUBLIC_GETTER) + var eventTransportService : EventTransportService + + /** Listener on platform events related to space participants. + * @since 0.10 + */ + @Accessors(PUBLIC_GETTER) + var spaceParticipantListener : SpaceParticipantListener + + /** Constructor. + * + * @param id identifier of the space. + * @param lockProvider the provider of synchronization locks. + * @param participantListener the platform listener on participant events. + * @param logger the logger service. + */ + new (spaceId : SpaceID, lockProvider : Provider, + participantListener : SpaceParticipantListener, logger : LoggingService) { + super(spaceId) + this.lock = lockProvider.get + this.spaceParticipantListener = participantListener + this.logger = logger + } + + /** Set the event router. + * + * @param router the router. + * @since 0.8 + */ + @Inject + def setEventTransportService(router : EventTransportService) { + this.eventTransportService = router + } + + protected final def registerToSpace(entity : EventListener) : Address { + assert entity !== null + var id = entity.ID + var address = new Address(this.spaceID, id) + var participant = Participant::createAndInit(address, entity) + val lck = getLock + lck.writeLock.lock + try { + this.internalParticipantStructure.put(id, participant) + } finally { + lck.writeLock.unlock + } + getSpaceParticipantListener?.participantJoined(participant) + return address + } + + protected final def unregisterFromSpace(entity : EventListener) : Address { + assert entity !== null + var participant : Participant = null + var becomesEmpty : boolean + val lck = getLock + lck.writeLock.lock + try { + var structure = this.internalParticipantStructure + participant = structure.remove(entity.ID) + becomesEmpty = structure.empty + } finally { + lck.writeLock.unlock + } + if (participant !== null) { + if (becomesEmpty) { + fireDestroyableSpace + } + getSpaceParticipantListener?.participantLeft(participant) + return participant.address + } + return null + } + + def getAddress(id : UUID) : Address { + assert id !== null + var participant : Participant = null + val lck = getLock + lck.writeLock.lock + try { + participant = this.internalParticipantStructure.remove(id) + } finally { + lck.writeLock.unlock + } + if (participant !== null) { + return participant.address + } + return null + } + + @Deprecated + final def getParticipants : SynchronizedSet { + val lck = getLock + lck.readLock.lock + try { + Collections3.unmodifiableSynchronizedSet(this.internalParticipantStructure.keySet, lck) + } finally { + lck.readLock.unlock + } + } + + @Deprecated + final def emit(^event : Event, scope : Scope

) { + emit(null, ^event, scope) + } + + final def emit(eventSource : UUID, ^event : Event, scope : Scope
) { + assert ^event !== null + ensureEventSource(eventSource, ^event) + assert this.spaceID == ^event.source.spaceID, "The source address must belong to this space" + try { + var mts = getEventTransportService + if (mts === null || mts.routeEvent(^event, this, scope)) { + ^event.emitLocally(scope) + } + } catch (e : Throwable) { + this.logger.kernelLogger.log(Level::SEVERE, MessageFormat::format(Messages::AbstractEventSpace_0, ^event, scope, e), e) + } + } + + /** Ensure that the given event has a source. + * + * @param eventSource the source of the event. + * @param event the event to emit. + * @since 0.6.0 + */ + protected def ensureEventSource(eventSource : UUID, ^event : Event) { + if (^event.source === null) { + if (eventSource !== null) { + ^event.source = new Address(spaceID, eventSource) + } else { + throw new AssertionError("Every event must have a source") + } + } + } + + private def lazyParticipant(address : Address) : Participant { + var links = SREutils::getSreSpecificData(address, typeof(AddressLazyLinks)) + if (links === null) { + links = new AddressLazyLinks + SREutils::setSreSpecificData(address, links) + } + var participant = links.participant + if (participant !== null) { + return participant + } + participant = this.internalParticipantStructure.get(address.UUID) + links.participant = participant + return participant + } + + /** Replies the participants that matches the given scope. + * + * @param scope the scope. + * @return the matching participants. + */ + protected def getScopedParticipants(scope : Scope) : Stream { + if (scope === null || scope === Scopes::allParticipants) { + return this.internalParticipantStructure.values.stream + } + if (scope instanceof AddressScope) { + return Stream.of(scope.addresses).map([it.lazyParticipant]).filter([it !== null]) + } + return this.internalParticipantStructure.values.stream.filter [ + scope.matches(it.address) + ] + } + + /** + * Do the emission of the event. + * + *

This function emits the event only on the internal event bus of the listeners. + * + *

This function launch a task for each matching listener. + * + * @param event the event to emit. + * @param scope description of the scope of the event, i.e. the receivers of the event. + */ + protected def emitLocally(^event : Event, scope : Scope) { + assert ^event !== null + // Distinguish the emit and reception processes into two different tasks. + // The emit process is run in the current thread. + // The reception process should be treated into a separate thread in order + // to never block the sender process. + val lck = getLock + lck.readLock.lock + try { + val participants = getScopedParticipants(scope) + participants.forEach [ + it.getParticipant.receiveEvent(^event) + ] + } finally { + lck.readLock.unlock + } + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/AbstractSpace.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/AbstractSpace.sarl new file mode 100644 index 0000000000..ffe6fdb368 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/AbstractSpace.sarl @@ -0,0 +1,97 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.spaces + +import io.sarl.lang.core.SRESpecificDataContainer +import io.sarl.lang.core.Space +import io.sarl.lang.core.SpaceID +import java.lang.ref.WeakReference +import java.util.EventListener + +/** + * Abstract implementation of a space. + * + * @author $Author: srodriguez$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +abstract class AbstractSpace extends SRESpecificDataContainer implements Space { + + val id : SpaceID + + var listener : WeakReference + + /** + * Constructs a space. + * + * @param id identifier of the space. + */ + new (id : SpaceID) { + this.id = id + } + + /** Change the listener on the space events. + */ + def setSpaceListenerIfNone(listener : SpaceListener) { + if (listener === null) { + this.listener = null + } else if (this.listener === null) { + this.listener = new WeakReference(listener) + } + } + + /** Fire the event that indicates the space could be destroyed. + */ + protected def fireDestroyableSpace() { + var listen = this.listener?.get + if (listen !== null) { + listen.destroyableSpace(this) + } + } + + def toString : String { + this.spaceID.toString + } + + final def getSpaceID : SpaceID { + this.id + } + +} + +/** + * Listener on events related to the space. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +interface SpaceListener extends EventListener { + /** + * Invoked when the space could be destroyed. + * + * @param space reference to the created space. + */ + def destroyableSpace(^space : Space) +} \ No newline at end of file diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/AddressLazyLinks.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/AddressLazyLinks.sarl new file mode 100644 index 0000000000..5f2dacc609 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/AddressLazyLinks.sarl @@ -0,0 +1,70 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.spaces + +import java.lang.ref.WeakReference + +/** + * Links from an address to another component of the SRE. + * + *

This class was introduced for increasing the run-time performances of the SRE. + * It should not be used directly. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.7.0 + */ +class AddressLazyLinks { + + var participant : WeakReference + + /** Replies the link to the participant. + * + * @return the link to the participant, {@code null} if none. + */ + def getParticipant : Participant { + this.participant?.get + } + + /** Change the link to the participant. + * + * @param participant the link to the participant, {@code null} if none. + */ + def setParticipant(participant : Participant) { + if (participant === null) { + this.participant = null; + } else { + this.participant = new WeakReference(participant) + } + } + + override toString : String { + var p = getParticipant + if (p !== null) { + return p. toString + } + return "" + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/EventTransportService.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/EventTransportService.sarl new file mode 100644 index 0000000000..fee5ac895e --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/EventTransportService.sarl @@ -0,0 +1,77 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.spaces + +import com.google.inject.ImplementedBy +import io.sarl.lang.core.Address +import io.sarl.lang.core.Event +import io.sarl.lang.core.EventSpace +import io.sarl.lang.core.Scope +import javax.inject.Singleton + +/** + * This service enables to route events + * + * @author $Author: srodriguez$ + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.8 + */ +@ImplementedBy(typeof(LocalEventTransportService)) +interface EventTransportService { + + /** + * Route the given event into + * + *

This function emits the event only on the internal event bus of the listeners. + * + *

This function launch a task for each matching listener. + * + * @param event the event to emit. + * @param ^space the local space in which the event should be routed. + * @param scope description of the scope of the event, i.e. the receivers of the event. + * @return {@code true} if the message should be also routed locally by the space instance itself. + * {@code false} if the space instance must not route the event. + */ + def routeEvent(^event : Event, ^space : EventSpace, scope : Scope) : boolean + +} + +/** + * This service enables to route events on the local machine only. + * + * @author $Author: srodriguez$ + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.8 + */ +@Singleton +class LocalEventTransportService implements EventTransportService { + + override routeEvent(^event : Event, ^space : EventSpace, scope : Scope) : boolean { + true + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/LocalEventSpaces.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/LocalEventSpaces.sarl new file mode 100644 index 0000000000..adf0a47197 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/LocalEventSpaces.sarl @@ -0,0 +1,155 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.spaces + +import io.sarl.core.OpenEventSpace +import io.sarl.lang.core.Address +import io.sarl.lang.core.EventListener +import io.sarl.lang.core.SpaceID +import io.sarl.sre.capacities.InformedEventListener +import io.sarl.sre.services.logging.LoggingService +import io.sarl.util.RestrictedAccessEventSpace +import java.security.AccessControlException +import java.security.Principal +import java.security.acl.Acl +import java.security.acl.Permission +import java.util.Map +import java.util.UUID +import java.util.concurrent.locks.ReadWriteLock +import javax.inject.Provider + +/** + * Default implementation of an event space which has a local repository. + * + * @author $Author: srodriguez$ + * @author $Author: ngaud$ + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class OpenLocalEventSpace extends AbstractEventSpace implements OpenEventSpace { + + val repository = newTreeMap(null) + + override getInternalParticipantStructure : Map { + this.repository + } + + def register(entity : EventListener) : Address { + entity.registerToSpace + } + + def unregister(entity : EventListener) : Address { + entity.unregisterFromSpace + } + +} + +/** + * Default implementation of a restricted-access event space. + * + * @author $Author: srodriguez$ + * @author $Author: ngaud$ + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class RestrictedAccessLocalEventSpace extends AbstractEventSpace implements RestrictedAccessEventSpace { + + val repository = newTreeMap(null) + + val acl : Acl + + val accessPermission : Permission + + /** + * Constructs an event space. + * + * @param id identifier of the space. + * @param acl Access Control List + * @param accessPermission permission that corresponds to the registration in the space. + * @param lockProvider the provider of synchronization locks. + * @param participantListener the platform listener on participant events. + * @param logger the logger service. + */ + new (id : SpaceID, acl : Acl, accessPermission : Permission, lockProvider : Provider, + participantListener : SpaceParticipantListener, logger : LoggingService) { + super(id, lockProvider, participantListener, logger) + assert acl !== null + assert accessPermission !== null + this.acl = acl + this.accessPermission = accessPermission + } + + override getInternalParticipantStructure : Map { + this.repository + } + + /** + * Replies the Access Control List. + * + * @return the acl. + */ + def getAccessControlList : Acl { + this.acl + } + + /** + * Replies the permission to register into this space. + * + * @return the permission. + */ + def getRegistrationPermission : Permission { + this.accessPermission + } + + def register(entity : EventListener, requester : Principal) : Address { + if (this.accessControlList.checkPermission(requester, this.registrationPermission)) { + return (entity as InformedEventListener).registerToSpace + } + throw new AccessControlException(Messages::RestrictedAccessLocalEventSpace_0) + } + + def register(entity : P) : Address with P extends EventListener & Principal { + if (this.accessControlList.checkPermission(entity, this.registrationPermission)) { + return (entity as InformedEventListener).registerToSpace + } + throw new AccessControlException(Messages::RestrictedAccessLocalEventSpace_0) + } + + /** + * Unregisters the entity inside this space. + * Before unregistering an agent, the Space should emit a MemberLeft + * event where the source is the address of the unregistered agent. + * + * @param entity the event listener to unregister. + * @return the former entity's address + * @fires ParticipantUnregistered + */ + def unregister(entity : EventListener) : Address { + entity.unregisterFromSpace + } + +} + diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/Messages.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/Messages.sarl new file mode 100644 index 0000000000..1e320c4637 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/Messages.sarl @@ -0,0 +1,49 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.spaces + +import org.eclipse.osgi.util.NLS + +/** Messages. + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +final class Messages extends NLS { + + @SuppressWarnings("unused_private_member") + static val BUNDLE_NAME = { + val name = typeof(Messages).getPackage.name + ".messages" + // initialize resource bundle + NLS.initializeMessages(name, typeof(Messages)) + name + } + + public static var AbstractEventSpace_0 : String + + public static var RestrictedAccessLocalEventSpace_0 : String + + private new { + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/Participant.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/Participant.sarl new file mode 100644 index 0000000000..287cce791e --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/Participant.sarl @@ -0,0 +1,94 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.spaces + +import io.sarl.lang.annotation.PrivateAPI +import io.sarl.lang.core.Address +import io.sarl.lang.core.EventListener +import io.sarl.lang.core.SREutils + +/** + * Description of a participant. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.6.0 + */ +class Participant { + + val participantAddress : Address + + val participant : EventListener + + /** Create a participant. + * + * @param address the address of the participant. + * @param eventListener the participant. + */ + @SuppressWarnings("use_reserved_sarl_annotation") + @PrivateAPI(isCallerOnly = true) + package static def createAndInit(address : Address, eventListener : EventListener) : Participant { + var participant = new Participant(address, eventListener) + var links = new AddressLazyLinks + links.participant = participant + SREutils::setSreSpecificData(address, links) + return participant + } + + /** Constructor. + * + * @param address the address of the participant. + * @param eventListener the participant. + */ + private new (address : Address, eventListener : EventListener) { + this.participantAddress = address + this.participant = eventListener + } + + /** Replies participant address. */ + def getAddress : Address { + this.participantAddress + } + + /** Replies the participant. */ + def getParticipant : EventListener { + this.participant + } + + override equals(obj : Object) : boolean { + if (obj instanceof Participant) { + return obj.address == address + } + return false + } + + override hashCode : int { + address.hashCode + } + + override toString : String { + this.participantAddress?.toString + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/SpaceParticipantListener.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/SpaceParticipantListener.sarl new file mode 100644 index 0000000000..71fad6f732 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/SpaceParticipantListener.sarl @@ -0,0 +1,50 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.spaces + +import java.util.EventListener + +/** + * A listener for the Java events on spaces. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +interface SpaceParticipantListener extends EventListener { + + /** + * Invoked when a participant has joined a space. + * + * @param participant the participant. + */ + def participantJoined(participant : Participant) + + /** + * Invoked when a participant has left a space. + * + * @param participant the participant. + */ + def participantLeft(participant : Participant) + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/SpaceParticipantListenerFactory.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/SpaceParticipantListenerFactory.sarl new file mode 100644 index 0000000000..040c1f534e --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/SpaceParticipantListenerFactory.sarl @@ -0,0 +1,38 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.spaces + +import io.sarl.lang.core.EventSpace +import java.util.logging.Logger + +/** Factory for a space participant event emitter. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +interface SpaceParticipantListenerFactory { + + def create(defaultSpace : EventSpace, logger : Logger) : SpaceParticipantListener + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/SpaceSpecifications.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/SpaceSpecifications.sarl new file mode 100644 index 0000000000..c1bbbdf499 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/SpaceSpecifications.sarl @@ -0,0 +1,182 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.spaces + +import com.google.inject.Injector +import io.sarl.core.OpenEventSpace +import io.sarl.core.OpenEventSpaceSpecification +import io.sarl.lang.core.EventSpace +import io.sarl.lang.core.EventSpaceSpecification +import io.sarl.lang.core.SpaceID +import io.sarl.sre.services.logging.LoggingService +import io.sarl.util.RestrictedAccessEventSpace +import io.sarl.util.RestrictedAccessEventSpaceSpecification +import java.security.acl.Acl +import java.security.acl.Permission +import java.util.concurrent.locks.ReadWriteLock +import javax.inject.Provider + +/** + * Abstract specification for standard event spaces into the SRE. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +abstract class AbstractSreEventSpaceSpecification { + + val injector : Injector + + val lockProvider : Provider + + val logger : LoggingService + + val spaceParticipantListenerFactory : SpaceParticipantListenerFactory + + val defaultSpace : OpenEventSpace + + /** Constructor. + * + * @param injector the injector to use for creating the space. + * @param lockProvider the provider of synchronization locks. + * @param defaultSpace the default space. + * @param spaceParticipantListenerFactory the factory of listeners on space participant events. + * @param logger the logging service. + */ + new (injector : Injector, lockProvider : Provider, + defaultSpace : OpenEventSpace, + spaceParticipantListenerFactory : SpaceParticipantListenerFactory, + logger : LoggingService) { + this.injector = injector; + this.lockProvider = lockProvider + this.defaultSpace = defaultSpace + this.spaceParticipantListenerFactory = spaceParticipantListenerFactory + this.logger = logger + } + + protected def createSpaceInstance(id : SpaceID, params : Object*) : OpenLocalEventSpace { + val listener = this.spaceParticipantListenerFactory.create(this.defaultSpace, this.logger.kernelLogger) + val ^space = new OpenLocalEventSpace(id, this.lockProvider, listener, this.logger) + this.injector.injectMembers(^space) + return ^space + } + +} + +/** + * Specification for standard event spaces into the SRE. + * + * @author $Author: srodriguez$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class SreEventSpaceSpecification extends AbstractSreEventSpaceSpecification implements EventSpaceSpecification { + + def ^create(id : SpaceID, params : Object*) : EventSpace { + id.createSpaceInstance(params) + } + +} + +/** + * Event space specification related to the SRE. + * + * @author $Author: srodriguez$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class SreOpenEventSpaceSpecification extends AbstractSreEventSpaceSpecification implements OpenEventSpaceSpecification { + + def ^create(id : SpaceID, params : Object*) : OpenEventSpace { + id.createSpaceInstance(params) + } + +} + +/** + * Default implementation of the specification of a restricted-access event space. + * + *

The initialization parameters of {@link #create(SpaceID, Object...)} must contain an instance of {@link Acl}. This instance is + * the Access Control List. The first parameter that is a {@link Permission} will be assumed as the permission to have to be + * allowed to access to the space. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + */ +class SreRestrictedAccessEventSpaceSpecification implements RestrictedAccessEventSpaceSpecification { + + var injector : Injector + + val lockProvider : Provider + + val logger : LoggingService + + val spaceParticipantListenerFactory : SpaceParticipantListenerFactory + + val defaultSpace : EventSpace + + /** Constructor. + * + * @param injector the injector to use for creating the space.. + * @param lockProvider the provider of synchronization locks. + * @param defaultSpace the default space. + * @param spaceParticipantListenerFactory the factory of listeners on space participant events. + * @param logger the logging service. + */ + new (injector : Injector, lockProvider : Provider, defaultSpace : OpenEventSpace, + spaceParticipantListenerFactory : SpaceParticipantListenerFactory, logger : LoggingService) { + this.injector = injector; + this.lockProvider = lockProvider + this.defaultSpace = defaultSpace + this.spaceParticipantListenerFactory = spaceParticipantListenerFactory + this.logger = logger + } + + def ^create(id : SpaceID, params : Object*) : RestrictedAccessEventSpace { + var acl : Acl = null + var permission : Permission = null + for (o : params) { + if (o instanceof Acl) { + acl = o + } else if (o instanceof Permission) { + permission = o + } + } + if (acl !== null) { + if (permission === null) { + permission = new RegistrationPermission + } + val listener = this.spaceParticipantListenerFactory.create(this.defaultSpace, this.logger.kernelLogger) + val ^space = new RestrictedAccessLocalEventSpace(id, acl, permission, this.lockProvider, listener, this.logger) + this.injector.injectMembers(^space) + return ^space + } + throw new IllegalArgumentException + } + +} diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/SpaceWithParticipants.sarl b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/SpaceWithParticipants.sarl new file mode 100644 index 0000000000..516af6dc16 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/SpaceWithParticipants.sarl @@ -0,0 +1,49 @@ +/* + * $Id$ + * + * SARL is an general-purpose agent programming language. + * More details on http://www.sarl.io + * + * Copyright (C) 2014-2019 the original authors or 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.spaces + +import io.sarl.lang.annotation.PrivateAPI +import io.sarl.lang.core.Space +import java.util.Map +import java.util.UUID + +/** + * Space with participants. + * + * @author $Author: sgalland$ + * @version $FullVersion$ + * @mavengroupid $GroupId$ + * @mavenartifactid $ArtifactId$ + * @since 0.10 + */ +@SuppressWarnings("use_reserved_sarl_annotation") +@PrivateAPI +interface SpaceWithParticipants extends Space { + + /** Replies the internal data structure for storing the space's participants. + * + * @return the entire participant structure. + */ + def getInternalParticipantStructure : Map + +} + diff --git a/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/messages.properties b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/messages.properties new file mode 100644 index 0000000000..bbdcea9423 --- /dev/null +++ b/sre/io.janusproject/io.janusproject.plugin/src/io/sarl/sre/spaces/messages.properties @@ -0,0 +1,2 @@ +AbstractEventSpace_0=Cannot emit the event ''{0}'' with the scope ''{1}'': {2}. +RestrictedAccessLocalEventSpace_0 = Access Denied