From 74812938ad3800e5b2807bfd22455716838e3e25 Mon Sep 17 00:00:00 2001 From: Omur Sahin Date: Fri, 21 Aug 2026 17:40:40 +0300 Subject: [PATCH] jasper starter --- jdk_25_maven/cs/rest/jasper/.dockerignore | 32 + jdk_25_maven/cs/rest/jasper/.editorconfig | 20 + jdk_25_maven/cs/rest/jasper/.gitattributes | 150 + jdk_25_maven/cs/rest/jasper/.gitignore | 35 + jdk_25_maven/cs/rest/jasper/.m2/settings.xml | 13 + .../.mvn/wrapper/maven-wrapper.properties | 3 + jdk_25_maven/cs/rest/jasper/Dockerfile | 89 + jdk_25_maven/cs/rest/jasper/LICENSE | 21 + jdk_25_maven/cs/rest/jasper/README.md | 1034 ++++ jdk_25_maven/cs/rest/jasper/_config.yml | 3 + .../head-custom-google-analytics.html | 9 + .../cs/rest/jasper/docker-compose.yaml | 29 + .../cs/rest/jasper/docker/entrypoint.sh | 38 + jdk_25_maven/cs/rest/jasper/docs/cursor.png | Bin 0 -> 39351 bytes jdk_25_maven/cs/rest/jasper/docs/entities.png | Bin 0 -> 138663 bytes jdk_25_maven/cs/rest/jasper/docs/origins.png | Bin 0 -> 105951 bytes .../jasper/docs/release-notes/jasper-1.0.md | 62 + .../jasper/docs/release-notes/jasper-1.1.md | 51 + .../jasper/docs/release-notes/jasper-1.2.md | 25 + .../cs/rest/jasper/gatling/.dockerignore | 9 + .../cs/rest/jasper/gatling/.gitignore | 2 + .../cs/rest/jasper/gatling/Dockerfile | 11 + jdk_25_maven/cs/rest/jasper/gatling/README.md | 142 + .../jasper/gatling/docker-compose.sqlite.yaml | 47 + .../rest/jasper/gatling/docker-compose.yaml | 67 + .../cs/rest/jasper/gatling/gen-index.sh | 195 + jdk_25_maven/cs/rest/jasper/gatling/pom.xml | 61 + .../src/test/gatling/conf/gatling.conf | 132 + .../gatling/src/test/gatling/conf/logback.xml | 24 + .../jasper/ComprehensiveSimulation.java | 471 ++ .../simulations/jasper/InfernoSimulation.java | 109 + .../jasper/SmokeTestSimulation.java | 222 + .../jasper/StressTestSimulation.java | 454 ++ .../jasper/UserJourneySimulation.java | 528 ++ .../src/test/resources/data/sources.csv | 9 + .../src/test/resources/data/topics.csv | 9 + jdk_25_maven/cs/rest/jasper/mvnw | 295 + jdk_25_maven/cs/rest/jasper/mvnw.cmd | 189 + jdk_25_maven/cs/rest/jasper/pom.xml | 531 ++ .../main/java/jasper/JasperApplication.java | 29 + .../src/main/java/jasper/aop/ClearIdle.java | 16 + .../src/main/java/jasper/aop/IdleAspect.java | 56 + .../jasper/aop/logging/LoggingAspect.java | 102 + .../main/java/jasper/client/JasperClient.java | 135 + .../main/java/jasper/client/OembedClient.java | 15 + .../main/java/jasper/client/ScimClient.java | 41 + .../main/java/jasper/client/TokenClient.java | 18 + .../java/jasper/client/dto/JasperMapper.java | 16 + .../java/jasper/component/AccessToken.java | 46 + .../main/java/jasper/component/Backup.java | 322 + .../java/jasper/component/ConfigCache.java | 345 ++ .../src/main/java/jasper/component/Fetch.java | 15 + .../java/jasper/component/FetchImplCache.java | 32 + .../java/jasper/component/FetchImplHttp.java | 118 + .../main/java/jasper/component/FileCache.java | 365 ++ .../main/java/jasper/component/HttpCache.java | 32 + .../jasper/component/HttpClientFactory.java | 117 + .../main/java/jasper/component/Images.java | 118 + .../main/java/jasper/component/Ingest.java | 258 + .../java/jasper/component/IngestBundle.java | 118 + .../main/java/jasper/component/IngestExt.java | 170 + .../java/jasper/component/IngestPlugin.java | 167 + .../java/jasper/component/IngestTemplate.java | 167 + .../java/jasper/component/IngestUser.java | 168 + .../main/java/jasper/component/Messages.java | 232 + .../src/main/java/jasper/component/Meta.java | 207 + .../jasper/component/OembedProviders.java | 75 + .../main/java/jasper/component/Preload.java | 111 + .../java/jasper/component/ProfileManager.java | 21 + .../jasper/component/ProfileManagerScim.java | 182 + .../src/main/java/jasper/component/Proxy.java | 125 + .../java/jasper/component/Replicator.java | 582 ++ .../src/main/java/jasper/component/Rng.java | 54 + .../main/java/jasper/component/Sanitizer.java | 210 + .../main/java/jasper/component/Scraper.java | 499 ++ .../component/ScriptExecutorFactory.java | 132 + .../java/jasper/component/ScriptRunner.java | 160 + .../main/java/jasper/component/Storage.java | 67 + .../jasper/component/StorageImplLocal.java | 308 + .../main/java/jasper/component/Tagger.java | 261 + .../java/jasper/component/TunnelClient.java | 350 ++ .../main/java/jasper/component/Validate.java | 355 ++ .../component/channel/ClearConfigCache.java | 77 + .../java/jasper/component/channel/Index.java | 93 + .../java/jasper/component/channel/Pull.java | 275 + .../java/jasper/component/channel/Push.java | 137 + .../java/jasper/component/channel/Stomp.java | 102 + .../component/channel/TunnelServer.java | 47 + .../channel/TunnelServerImplFile.java | 91 + .../channel/TunnelServerImplK8s.java | 90 + .../java/jasper/component/channel/Watch.java | 111 + .../java/jasper/component/cron/Backfill.java | 63 + .../main/java/jasper/component/cron/Cron.java | 233 + .../component/cron/ExpireScriptDeps.java | 68 + .../main/java/jasper/component/cron/Pull.java | 38 + .../main/java/jasper/component/cron/Push.java | 38 + .../java/jasper/component/cron/Recycler.java | 29 + .../java/jasper/component/cron/Script.java | 71 + .../java/jasper/component/delta/Async.java | 194 + .../jasper/component/delta/DeltaScript.java | 76 + .../java/jasper/component/delta/Mail.java | 124 + .../component/dto/AuthTokenResponse.java | 13 + .../java/jasper/component/dto/Bundle.java | 19 + .../component/dto/ComponentDtoMapper.java | 81 + .../java/jasper/component/dto/JsonLd.java | 46 + .../component/dto/MetadataUpdateDto.java | 22 + .../jasper/component/dto/RefUpdateDto.java | 37 + .../jasper/component/dto/ScimPatchOp.java | 34 + .../component/dto/ScimUserResource.java | 28 + .../jasper/component/script/DeltaCache.java | 36 + .../jasper/component/script/DeltaScrape.java | 71 + .../jasper/component/script/RssParser.java | 427 ++ .../component/script/ScriptDefaults.java | 34 + .../java/jasper/component/vm/JavaScript.java | 71 + .../main/java/jasper/component/vm/Python.java | 118 + .../java/jasper/component/vm/RunProcess.java | 83 + .../main/java/jasper/component/vm/Shell.java | 59 + .../jasper/config/AsyncConfiguration.java | 61 + .../main/java/jasper/config/AuthConfig.java | 71 + .../jasper/config/BulkheadConfiguration.java | 89 + .../main/java/jasper/config/CacheConfig.java | 105 + .../src/main/java/jasper/config/Config.java | 401 ++ .../main/java/jasper/config/Constants.java | 11 + .../jasper/config/DatabaseConfiguration.java | 12 + .../config/DateTimeFormatConfiguration.java | 20 + .../jasper/config/FeignConfiguration.java | 32 + .../java/jasper/config/IntegrationConfig.java | 93 + .../jasper/config/JacksonConfiguration.java | 95 + .../java/jasper/config/KubernetesConfig.java | 10 + .../jasper/config/LocaleConfiguration.java | 17 + .../config/LoggingAspectConfiguration.java | 19 + .../java/jasper/config/MetricsConfig.java | 67 + .../jasper/config/ObjectMapperConfig.java | 18 + .../jasper/config/OpenApiConfiguration.java | 34 + .../java/jasper/config/PostgreSQLDialect.java | 45 + .../src/main/java/jasper/config/Props.java | 434 ++ .../java/jasper/config/RateLimitConfig.java | 114 + .../main/java/jasper/config/RedisConfig.java | 529 ++ .../main/java/jasper/config/SQLiteConfig.java | 114 + .../java/jasper/config/SQLiteDialect.java | 208 + .../jasper/config/SecurityConfiguration.java | 220 + .../java/jasper/config/SingleNodeConfig.java | 126 + .../StaticResourcesWebConfiguration.java | 54 + .../java/jasper/config/WebConfigurer.java | 102 + .../java/jasper/config/WebSocketConfig.java | 304 + .../jasper/domain/AbstractAuditingEntity.java | 77 + .../src/main/java/jasper/domain/Ext.java | 76 + .../src/main/java/jasper/domain/External.java | 23 + .../src/main/java/jasper/domain/Metadata.java | 132 + .../src/main/java/jasper/domain/Plugin.java | 95 + .../src/main/java/jasper/domain/Ref.java | 264 + .../src/main/java/jasper/domain/RefId.java | 31 + .../src/main/java/jasper/domain/TagId.java | 32 + .../src/main/java/jasper/domain/Template.java | 92 + .../src/main/java/jasper/domain/User.java | 210 + .../main/java/jasper/domain/proj/Cursor.java | 7 + .../java/jasper/domain/proj/HasOrigin.java | 89 + .../main/java/jasper/domain/proj/HasTags.java | 76 + .../main/java/jasper/domain/proj/RefUrl.java | 13 + .../main/java/jasper/domain/proj/RefView.java | 19 + .../src/main/java/jasper/domain/proj/Tag.java | 154 + .../jasper/domain/validator/SchemaValid.java | 24 + .../domain/validator/SchemaValidator.java | 24 + .../jasper/errors/AlreadyExistsException.java | 11 + .../errors/DeactivateSelfException.java | 11 + .../DuplicateModifiedDateException.java | 11 + .../jasper/errors/DuplicateTagException.java | 14 + .../jasper/errors/FreshLoginException.java | 11 + .../jasper/errors/InvalidPatchException.java | 12 + .../jasper/errors/InvalidPluginException.java | 16 + .../errors/InvalidPluginUserUrlException.java | 14 + .../jasper/errors/InvalidPushException.java | 11 + .../errors/InvalidTemplateException.java | 16 + .../jasper/errors/InvalidTunnelException.java | 16 + .../errors/InvalidUserProfileException.java | 12 + .../jasper/errors/MaxSourcesException.java | 12 + .../java/jasper/errors/ModifiedException.java | 11 + .../jasper/errors/NotAvailableException.java | 8 + .../java/jasper/errors/NotFoundException.java | 11 + .../OperationForbiddenOnOriginException.java | 11 + .../jasper/errors/PublishDateException.java | 12 + .../jasper/errors/PullLocalException.java | 11 + .../errors/RetryableTunnelException.java | 16 + .../errors/ScrapeProtocolException.java | 12 + .../java/jasper/errors/ScriptException.java | 14 + .../java/jasper/errors/TooLargeException.java | 12 + .../errors/UntrustedScriptException.java | 17 + .../jasper/errors/UserTagInUseException.java | 11 + .../management/SecurityMetersService.java | 63 + .../src/main/java/jasper/plugin/Audio.java | 21 + .../src/main/java/jasper/plugin/Cache.java | 48 + .../src/main/java/jasper/plugin/Cron.java | 23 + .../src/main/java/jasper/plugin/Embed.java | 23 + .../src/main/java/jasper/plugin/Feed.java | 36 + .../src/main/java/jasper/plugin/Oembed.java | 30 + .../src/main/java/jasper/plugin/Origin.java | 27 + .../src/main/java/jasper/plugin/Pull.java | 55 + .../src/main/java/jasper/plugin/Push.java | 26 + .../src/main/java/jasper/plugin/Scrape.java | 30 + .../main/java/jasper/plugin/Thumbnail.java | 36 + .../src/main/java/jasper/plugin/Tunnel.java | 27 + .../src/main/java/jasper/plugin/Video.java | 21 + .../main/java/jasper/plugin/config/Index.java | 29 + .../java/jasper/plugin/config/Script.java | 25 + .../jasper/repository/BackfillRepository.java | 9 + .../BackfillRepositoryImplPostgres.java | 47 + .../BackfillRepositoryImplSqlite.java | 18 + .../java/jasper/repository/ExtRepository.java | 52 + .../jasper/repository/IndexRepository.java | 22 + .../IndexRepositoryImplPostgres.java | 86 + .../repository/IndexRepositoryImplSqlite.java | 87 + .../jasper/repository/ModifiedCursor.java | 10 + .../java/jasper/repository/OriginMixin.java | 14 + .../jasper/repository/PluginRepository.java | 65 + .../jasper/repository/QualifiedTagMixin.java | 16 + .../java/jasper/repository/RefRepository.java | 200 + .../repository/RefRepositoryCustom.java | 13 + .../repository/RefRepositoryImplPostgres.java | 65 + .../repository/RefRepositoryImplSqlite.java | 62 + .../java/jasper/repository/StreamMixin.java | 30 + .../jasper/repository/TemplateRepository.java | 93 + .../jasper/repository/UserRepository.java | 97 + .../java/jasper/repository/filter/Query.java | 20 + .../jasper/repository/filter/RefFilter.java | 158 + .../jasper/repository/filter/TagFilter.java | 81 + .../jasper/repository/filter/TagQuery.java | 103 + .../java/jasper/repository/spec/ExtSpec.java | 57 + .../jasper/repository/spec/OriginSpec.java | 54 + .../jasper/repository/spec/PluginSpec.java | 58 + .../jasper/repository/spec/QualifiedTag.java | 199 + .../java/jasper/repository/spec/RefSpec.java | 379 ++ .../repository/spec/ReplicationSpec.java | 28 + .../java/jasper/repository/spec/SortSpec.java | 147 + .../java/jasper/repository/spec/TagSpec.java | 127 + .../jasper/repository/spec/TemplateSpec.java | 58 + .../java/jasper/repository/spec/UserSpec.java | 63 + .../src/main/java/jasper/security/Auth.java | 1145 ++++ .../java/jasper/security/AuthFactory.java | 26 + .../jasper/security/AuthoritiesConstants.java | 19 + .../main/java/jasper/security/HostCheck.java | 51 + .../java/jasper/security/SecurityUtils.java | 101 + .../security/SpringSecurityAuditorAware.java | 19 + .../security/jwt/AbstractTokenProvider.java | 81 + .../main/java/jasper/security/jwt/Claims.java | 43 + .../jasper/security/jwt/JWTConfigurer.java | 28 + .../java/jasper/security/jwt/JWTFilter.java | 88 + .../security/jwt/JwtAuthentication.java | 58 + .../jasper/security/jwt/TokenProvider.java | 8 + .../security/jwt/TokenProviderImpl.java | 267 + .../jwt/TokenProviderImplDefault.java | 45 + .../security/jwt/TokenProviderImplNop.java | 23 + .../java/jasper/service/BackupService.java | 151 + .../main/java/jasper/service/ExtService.java | 149 + .../java/jasper/service/GraphService.java | 40 + .../java/jasper/service/OembedService.java | 61 + .../java/jasper/service/OriginService.java | 99 + .../java/jasper/service/PluginService.java | 139 + .../java/jasper/service/ProfileService.java | 109 + .../java/jasper/service/ProxyService.java | 108 + .../main/java/jasper/service/RefService.java | 197 + .../java/jasper/service/ScrapeService.java | 42 + .../main/java/jasper/service/SmtpService.java | 142 + .../java/jasper/service/TaggingService.java | 171 + .../java/jasper/service/TemplateService.java | 138 + .../main/java/jasper/service/UserService.java | 210 + .../java/jasper/service/dto/BackupDto.java | 19 + .../jasper/service/dto/BackupOptionsDto.java | 26 + .../java/jasper/service/dto/DtoMapper.java | 118 + .../main/java/jasper/service/dto/ExtDto.java | 23 + .../java/jasper/service/dto/ExternalDto.java | 17 + .../java/jasper/service/dto/MetadataDto.java | 24 + .../java/jasper/service/dto/PluginDto.java | 27 + .../java/jasper/service/dto/ProfileDto.java | 22 + .../main/java/jasper/service/dto/RefDto.java | 31 + .../java/jasper/service/dto/RefNodeDto.java | 32 + .../java/jasper/service/dto/RefReplDto.java | 30 + .../java/jasper/service/dto/RolesDto.java | 19 + .../jasper/service/dto/SmtpWebhookDto.java | 73 + .../java/jasper/service/dto/TemplateDto.java | 26 + .../main/java/jasper/service/dto/UserDto.java | 30 + .../src/main/java/jasper/util/Crypto.java | 46 + .../main/java/jasper/util/DbConstraint.java | 55 + .../util/JsonArrayStreamDataSupplier.java | 99 + .../src/main/java/jasper/util/Logging.java | 10 + .../jasper/web/rest/BackupController.java | 184 + .../java/jasper/web/rest/ExtController.java | 184 + .../java/jasper/web/rest/GraphController.java | 62 + .../jasper/web/rest/OEmbedController.java | 53 + .../jasper/web/rest/OriginController.java | 62 + .../jasper/web/rest/PluginController.java | 157 + .../jasper/web/rest/ProfileController.java | 140 + .../java/jasper/web/rest/ProxyController.java | 214 + .../java/jasper/web/rest/RefController.java | 292 + .../jasper/web/rest/ReplicateController.java | 467 ++ .../jasper/web/rest/ScrapeController.java | 61 + .../jasper/web/rest/TaggingController.java | 173 + .../jasper/web/rest/TemplateController.java | 157 + .../java/jasper/web/rest/UserController.java | 184 + .../jasper/web/rest/WebhookController.java | 50 + .../rest/errors/BadRequestAlertException.java | 42 + .../web/rest/errors/ErrorConstants.java | 14 + .../web/rest/errors/ExceptionTranslator.java | 257 + .../jasper/web/rest/errors/FieldErrorVM.java | 32 + .../rest/errors/ProblemDetailWithCause.java | 118 + .../rest/jasper/src/main/resources/banner.txt | 10 + .../resources/config/application-dblog.yml | 12 + .../resources/config/application-debug.yml | 16 + .../main/resources/config/application-dev.yml | 97 + .../resources/config/application-prod.yml | 92 + .../resources/config/application-sqlite.yml | 27 + .../main/resources/config/application-ssl.yml | 7 + .../main/resources/config/application-tls.yml | 19 + .../src/main/resources/config/application.yml | 238 + .../00000000000000_initial_schema.xml | 163 + .../00000000000001_sqlite_schema.xml | 149 + .../resources/config/liquibase/master.xml | 27 + .../main/resources/config/tls/keystore.p12 | Bin 0 -> 2721 bytes .../main/resources/i18n/messages.properties | 6 + .../resources/i18n/messages_en.properties | 6 + .../jasper/src/main/resources/keystore.p12 | Bin 0 -> 2652 bytes .../jasper/src/main/resources/providers.json | 5431 +++++++++++++++++ .../jasper/src/main/resources/swagger/api.yml | 3027 +++++++++ .../test/java/jasper/DisabledOnSqlite.java | 14 + .../src/test/java/jasper/IntegrationTest.java | 14 + .../jasper/MultiTenantIntegrationTest.java | 19 + .../java/jasper/TechnicalStructureTest.java | 53 + .../jasper/component/BackupRestoreIT.java | 487 ++ .../java/jasper/component/ConfigCacheIT.java | 315 + .../java/jasper/component/IngestExtIT.java | 232 + .../test/java/jasper/component/IngestIT.java | 561 ++ .../java/jasper/component/IngestPluginIT.java | 232 + .../jasper/component/IngestTemplateIT.java | 232 + .../java/jasper/component/IngestUserIT.java | 232 + .../test/java/jasper/component/MetaIT.java | 289 + .../java/jasper/component/ScriptRunnerIT.java | 381 ++ .../test/java/jasper/component/TaggerIT.java | 179 + .../java/jasper/component/ValidateExtIT.java | 340 ++ .../jasper/component/ValidateExtTest.java | 142 + .../java/jasper/component/ValidateRefIT.java | 457 ++ .../jasper/component/ValidateRefTest.java | 218 + .../jasper/component/channel/StompTest.java | 97 + .../java/jasper/component/cron/ScriptIT.java | 184 + .../jasper/component/delta/DeltaScriptIT.java | 184 + .../component/script/RssParserTest.java | 218 + .../jasper/component/vm/JavaScriptTest.java | 98 + .../java/jasper/component/vm/PythonTest.java | 116 + .../java/jasper/component/vm/ShellTest.java | 98 + .../jasper/config/AsyncConfigurationTest.java | 68 + .../test/java/jasper/config/ConfigTest.java | 282 + ...ernetesDiscoveryAutoConfigurationTest.java | 23 + .../LiquibasePropertiesBindingTest.java | 39 + .../StaticResourcesWebConfigurerTest.java | 45 + .../java/jasper/config/WebConfigurerTest.java | 154 + .../config/WebConfigurerTestController.java | 14 + .../jasper/config/WebSocketConfigTest.java | 185 + .../config/timezone/HibernateTimeZoneIT.java | 168 + .../java/jasper/domain/RefValidationTest.java | 150 + .../src/test/java/jasper/domain/UserTest.java | 399 ++ .../SecurityMetersServiceTests.java | 85 + .../repository/BackfillRepositoryIT.java | 129 + .../jasper/repository/IndexRepositoryIT.java | 40 + .../jasper/repository/RefRepositoryIT.java | 457 ++ .../repository/UserRepositoryCustomIT.java | 89 + .../repository/UserRepositoryMergeIT.java | 323 + .../repository/timezone/DateTimeWrapper.java | 134 + .../timezone/DateTimeWrapperRepository.java | 10 + .../security/AuthMultiTenantUnitTest.java | 1202 ++++ .../java/jasper/security/AuthUnitTest.java | 1176 ++++ .../security/SecurityUtilsUnitTest.java | 100 + .../jasper/security/jwt/JWTFilterTest.java | 161 + .../security/jwt/TokenProviderImplTest.java | 427 ++ .../jwt/TokenProviderSecurityMetersTests.java | 174 + .../java/jasper/service/ExtServiceIT.java | 708 +++ .../java/jasper/service/ExtServiceMTIT.java | 715 +++ .../java/jasper/service/PluginServiceIT.java | 169 + .../java/jasper/service/RefServiceIT.java | 1732 ++++++ .../java/jasper/service/RefServiceMTIT.java | 1469 +++++ .../java/jasper/service/TaggingServiceIT.java | 361 ++ .../jasper/service/TemplateServiceIT.java | 160 + .../java/jasper/service/UserServiceIT.java | 1062 ++++ .../java/jasper/web/rest/ExtControllerIT.java | 105 + .../jasper/web/rest/PluginControllerIT.java | 129 + .../jasper/web/rest/ProxyControllerIT.java | 316 + .../java/jasper/web/rest/RefControllerIT.java | 281 + .../jasper/web/rest/TaggingControllerIT.java | 359 ++ .../jasper/web/rest/TemplateControllerIT.java | 98 + .../jasper/web/rest/UserControllerIT.java | 101 + .../rest/errors/ExceptionTranslatorIT.java | 115 + .../ExceptionTranslatorTestController.java | 66 + .../resources/config/application-sqlite.yml | 26 + .../src/test/resources/config/application.yml | 113 + .../test/resources/junit-platform.properties | 3 + .../jasper/src/test/resources/providers.json | 18 + 393 files changed, 59615 insertions(+) create mode 100644 jdk_25_maven/cs/rest/jasper/.dockerignore create mode 100644 jdk_25_maven/cs/rest/jasper/.editorconfig create mode 100644 jdk_25_maven/cs/rest/jasper/.gitattributes create mode 100644 jdk_25_maven/cs/rest/jasper/.gitignore create mode 100644 jdk_25_maven/cs/rest/jasper/.m2/settings.xml create mode 100644 jdk_25_maven/cs/rest/jasper/.mvn/wrapper/maven-wrapper.properties create mode 100644 jdk_25_maven/cs/rest/jasper/Dockerfile create mode 100644 jdk_25_maven/cs/rest/jasper/LICENSE create mode 100644 jdk_25_maven/cs/rest/jasper/README.md create mode 100644 jdk_25_maven/cs/rest/jasper/_config.yml create mode 100644 jdk_25_maven/cs/rest/jasper/_includes/head-custom-google-analytics.html create mode 100644 jdk_25_maven/cs/rest/jasper/docker-compose.yaml create mode 100644 jdk_25_maven/cs/rest/jasper/docker/entrypoint.sh create mode 100644 jdk_25_maven/cs/rest/jasper/docs/cursor.png create mode 100644 jdk_25_maven/cs/rest/jasper/docs/entities.png create mode 100644 jdk_25_maven/cs/rest/jasper/docs/origins.png create mode 100644 jdk_25_maven/cs/rest/jasper/docs/release-notes/jasper-1.0.md create mode 100644 jdk_25_maven/cs/rest/jasper/docs/release-notes/jasper-1.1.md create mode 100644 jdk_25_maven/cs/rest/jasper/docs/release-notes/jasper-1.2.md create mode 100644 jdk_25_maven/cs/rest/jasper/gatling/.dockerignore create mode 100644 jdk_25_maven/cs/rest/jasper/gatling/.gitignore create mode 100644 jdk_25_maven/cs/rest/jasper/gatling/Dockerfile create mode 100644 jdk_25_maven/cs/rest/jasper/gatling/README.md create mode 100644 jdk_25_maven/cs/rest/jasper/gatling/docker-compose.sqlite.yaml create mode 100644 jdk_25_maven/cs/rest/jasper/gatling/docker-compose.yaml create mode 100644 jdk_25_maven/cs/rest/jasper/gatling/gen-index.sh create mode 100644 jdk_25_maven/cs/rest/jasper/gatling/pom.xml create mode 100644 jdk_25_maven/cs/rest/jasper/gatling/src/test/gatling/conf/gatling.conf create mode 100644 jdk_25_maven/cs/rest/jasper/gatling/src/test/gatling/conf/logback.xml create mode 100644 jdk_25_maven/cs/rest/jasper/gatling/src/test/java/simulations/jasper/ComprehensiveSimulation.java create mode 100644 jdk_25_maven/cs/rest/jasper/gatling/src/test/java/simulations/jasper/InfernoSimulation.java create mode 100644 jdk_25_maven/cs/rest/jasper/gatling/src/test/java/simulations/jasper/SmokeTestSimulation.java create mode 100644 jdk_25_maven/cs/rest/jasper/gatling/src/test/java/simulations/jasper/StressTestSimulation.java create mode 100644 jdk_25_maven/cs/rest/jasper/gatling/src/test/java/simulations/jasper/UserJourneySimulation.java create mode 100644 jdk_25_maven/cs/rest/jasper/gatling/src/test/resources/data/sources.csv create mode 100644 jdk_25_maven/cs/rest/jasper/gatling/src/test/resources/data/topics.csv create mode 100644 jdk_25_maven/cs/rest/jasper/mvnw create mode 100644 jdk_25_maven/cs/rest/jasper/mvnw.cmd create mode 100644 jdk_25_maven/cs/rest/jasper/pom.xml create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/JasperApplication.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/aop/ClearIdle.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/aop/IdleAspect.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/aop/logging/LoggingAspect.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/client/JasperClient.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/client/OembedClient.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/client/ScimClient.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/client/TokenClient.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/client/dto/JasperMapper.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/AccessToken.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/Backup.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/ConfigCache.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/Fetch.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/FetchImplCache.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/FetchImplHttp.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/FileCache.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/HttpCache.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/HttpClientFactory.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/Images.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/Ingest.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/IngestBundle.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/IngestExt.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/IngestPlugin.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/IngestTemplate.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/IngestUser.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/Messages.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/Meta.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/OembedProviders.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/Preload.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/ProfileManager.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/ProfileManagerScim.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/Proxy.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/Replicator.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/Rng.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/Sanitizer.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/Scraper.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/ScriptExecutorFactory.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/ScriptRunner.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/Storage.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/StorageImplLocal.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/Tagger.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/TunnelClient.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/Validate.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/channel/ClearConfigCache.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/channel/Index.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/channel/Pull.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/channel/Push.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/channel/Stomp.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/channel/TunnelServer.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/channel/TunnelServerImplFile.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/channel/TunnelServerImplK8s.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/channel/Watch.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/cron/Backfill.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/cron/Cron.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/cron/ExpireScriptDeps.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/cron/Pull.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/cron/Push.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/cron/Recycler.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/cron/Script.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/delta/Async.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/delta/DeltaScript.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/delta/Mail.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/dto/AuthTokenResponse.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/dto/Bundle.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/dto/ComponentDtoMapper.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/dto/JsonLd.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/dto/MetadataUpdateDto.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/dto/RefUpdateDto.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/dto/ScimPatchOp.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/dto/ScimUserResource.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/script/DeltaCache.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/script/DeltaScrape.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/script/RssParser.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/script/ScriptDefaults.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/vm/JavaScript.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/vm/Python.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/vm/RunProcess.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/component/vm/Shell.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/config/AsyncConfiguration.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/config/AuthConfig.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/config/BulkheadConfiguration.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/config/CacheConfig.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/config/Config.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/config/Constants.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/config/DatabaseConfiguration.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/config/DateTimeFormatConfiguration.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/config/FeignConfiguration.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/config/IntegrationConfig.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/config/JacksonConfiguration.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/config/KubernetesConfig.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/config/LocaleConfiguration.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/config/LoggingAspectConfiguration.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/config/MetricsConfig.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/config/ObjectMapperConfig.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/config/OpenApiConfiguration.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/config/PostgreSQLDialect.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/config/Props.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/config/RateLimitConfig.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/config/RedisConfig.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/config/SQLiteConfig.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/config/SQLiteDialect.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/config/SecurityConfiguration.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/config/SingleNodeConfig.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/config/StaticResourcesWebConfiguration.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/config/WebConfigurer.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/config/WebSocketConfig.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/domain/AbstractAuditingEntity.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/domain/Ext.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/domain/External.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/domain/Metadata.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/domain/Plugin.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/domain/Ref.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/domain/RefId.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/domain/TagId.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/domain/Template.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/domain/User.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/domain/proj/Cursor.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/domain/proj/HasOrigin.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/domain/proj/HasTags.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/domain/proj/RefUrl.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/domain/proj/RefView.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/domain/proj/Tag.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/domain/validator/SchemaValid.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/domain/validator/SchemaValidator.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/errors/AlreadyExistsException.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/errors/DeactivateSelfException.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/errors/DuplicateModifiedDateException.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/errors/DuplicateTagException.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/errors/FreshLoginException.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/errors/InvalidPatchException.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/errors/InvalidPluginException.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/errors/InvalidPluginUserUrlException.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/errors/InvalidPushException.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/errors/InvalidTemplateException.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/errors/InvalidTunnelException.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/errors/InvalidUserProfileException.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/errors/MaxSourcesException.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/errors/ModifiedException.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/errors/NotAvailableException.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/errors/NotFoundException.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/errors/OperationForbiddenOnOriginException.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/errors/PublishDateException.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/errors/PullLocalException.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/errors/RetryableTunnelException.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/errors/ScrapeProtocolException.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/errors/ScriptException.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/errors/TooLargeException.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/errors/UntrustedScriptException.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/errors/UserTagInUseException.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/management/SecurityMetersService.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/plugin/Audio.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/plugin/Cache.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/plugin/Cron.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/plugin/Embed.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/plugin/Feed.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/plugin/Oembed.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/plugin/Origin.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/plugin/Pull.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/plugin/Push.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/plugin/Scrape.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/plugin/Thumbnail.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/plugin/Tunnel.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/plugin/Video.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/plugin/config/Index.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/plugin/config/Script.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/BackfillRepository.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/BackfillRepositoryImplPostgres.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/BackfillRepositoryImplSqlite.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/ExtRepository.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/IndexRepository.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/IndexRepositoryImplPostgres.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/IndexRepositoryImplSqlite.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/ModifiedCursor.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/OriginMixin.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/PluginRepository.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/QualifiedTagMixin.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/RefRepository.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/RefRepositoryCustom.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/RefRepositoryImplPostgres.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/RefRepositoryImplSqlite.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/StreamMixin.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/TemplateRepository.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/UserRepository.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/filter/Query.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/filter/RefFilter.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/filter/TagFilter.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/filter/TagQuery.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/spec/ExtSpec.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/spec/OriginSpec.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/spec/PluginSpec.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/spec/QualifiedTag.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/spec/RefSpec.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/spec/ReplicationSpec.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/spec/SortSpec.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/spec/TagSpec.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/spec/TemplateSpec.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/repository/spec/UserSpec.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/security/Auth.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/security/AuthFactory.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/security/AuthoritiesConstants.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/security/HostCheck.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/security/SecurityUtils.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/security/SpringSecurityAuditorAware.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/security/jwt/AbstractTokenProvider.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/security/jwt/Claims.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/security/jwt/JWTConfigurer.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/security/jwt/JWTFilter.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/security/jwt/JwtAuthentication.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/security/jwt/TokenProvider.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/security/jwt/TokenProviderImpl.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/security/jwt/TokenProviderImplDefault.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/security/jwt/TokenProviderImplNop.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/service/BackupService.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/service/ExtService.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/service/GraphService.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/service/OembedService.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/service/OriginService.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/service/PluginService.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/service/ProfileService.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/service/ProxyService.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/service/RefService.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/service/ScrapeService.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/service/SmtpService.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/service/TaggingService.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/service/TemplateService.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/service/UserService.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/service/dto/BackupDto.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/service/dto/BackupOptionsDto.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/service/dto/DtoMapper.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/service/dto/ExtDto.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/service/dto/ExternalDto.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/service/dto/MetadataDto.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/service/dto/PluginDto.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/service/dto/ProfileDto.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/service/dto/RefDto.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/service/dto/RefNodeDto.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/service/dto/RefReplDto.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/service/dto/RolesDto.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/service/dto/SmtpWebhookDto.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/service/dto/TemplateDto.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/service/dto/UserDto.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/util/Crypto.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/util/DbConstraint.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/util/JsonArrayStreamDataSupplier.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/util/Logging.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/web/rest/BackupController.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/web/rest/ExtController.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/web/rest/GraphController.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/web/rest/OEmbedController.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/web/rest/OriginController.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/web/rest/PluginController.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/web/rest/ProfileController.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/web/rest/ProxyController.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/web/rest/RefController.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/web/rest/ReplicateController.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/web/rest/ScrapeController.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/web/rest/TaggingController.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/web/rest/TemplateController.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/web/rest/UserController.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/web/rest/WebhookController.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/web/rest/errors/BadRequestAlertException.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/web/rest/errors/ErrorConstants.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/web/rest/errors/ExceptionTranslator.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/web/rest/errors/FieldErrorVM.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/java/jasper/web/rest/errors/ProblemDetailWithCause.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/resources/banner.txt create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/resources/config/application-dblog.yml create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/resources/config/application-debug.yml create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/resources/config/application-dev.yml create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/resources/config/application-prod.yml create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/resources/config/application-sqlite.yml create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/resources/config/application-ssl.yml create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/resources/config/application-tls.yml create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/resources/config/application.yml create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/resources/config/liquibase/changelog/00000000000001_sqlite_schema.xml create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/resources/config/liquibase/master.xml create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/resources/config/tls/keystore.p12 create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/resources/i18n/messages.properties create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/resources/i18n/messages_en.properties create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/resources/keystore.p12 create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/resources/providers.json create mode 100644 jdk_25_maven/cs/rest/jasper/src/main/resources/swagger/api.yml create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/DisabledOnSqlite.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/IntegrationTest.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/MultiTenantIntegrationTest.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/TechnicalStructureTest.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/component/BackupRestoreIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/component/ConfigCacheIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/component/IngestExtIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/component/IngestIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/component/IngestPluginIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/component/IngestTemplateIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/component/IngestUserIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/component/MetaIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/component/ScriptRunnerIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/component/TaggerIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/component/ValidateExtIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/component/ValidateExtTest.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/component/ValidateRefIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/component/ValidateRefTest.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/component/channel/StompTest.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/component/cron/ScriptIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/component/delta/DeltaScriptIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/component/script/RssParserTest.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/component/vm/JavaScriptTest.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/component/vm/PythonTest.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/component/vm/ShellTest.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/config/AsyncConfigurationTest.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/config/ConfigTest.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/config/JasperApplicationKubernetesDiscoveryAutoConfigurationTest.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/config/LiquibasePropertiesBindingTest.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/config/StaticResourcesWebConfigurerTest.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/config/WebConfigurerTest.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/config/WebConfigurerTestController.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/config/WebSocketConfigTest.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/config/timezone/HibernateTimeZoneIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/domain/RefValidationTest.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/domain/UserTest.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/management/SecurityMetersServiceTests.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/repository/BackfillRepositoryIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/repository/IndexRepositoryIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/repository/RefRepositoryIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/repository/UserRepositoryCustomIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/repository/UserRepositoryMergeIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/repository/timezone/DateTimeWrapper.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/repository/timezone/DateTimeWrapperRepository.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/security/AuthMultiTenantUnitTest.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/security/AuthUnitTest.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/security/SecurityUtilsUnitTest.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/security/jwt/JWTFilterTest.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/security/jwt/TokenProviderImplTest.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/security/jwt/TokenProviderSecurityMetersTests.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/service/ExtServiceIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/service/ExtServiceMTIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/service/PluginServiceIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/service/RefServiceIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/service/RefServiceMTIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/service/TaggingServiceIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/service/TemplateServiceIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/service/UserServiceIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/web/rest/ExtControllerIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/web/rest/PluginControllerIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/web/rest/ProxyControllerIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/web/rest/RefControllerIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/web/rest/TaggingControllerIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/web/rest/TemplateControllerIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/web/rest/UserControllerIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/web/rest/errors/ExceptionTranslatorIT.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/java/jasper/web/rest/errors/ExceptionTranslatorTestController.java create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/resources/config/application-sqlite.yml create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/resources/config/application.yml create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/resources/junit-platform.properties create mode 100644 jdk_25_maven/cs/rest/jasper/src/test/resources/providers.json diff --git a/jdk_25_maven/cs/rest/jasper/.dockerignore b/jdk_25_maven/cs/rest/jasper/.dockerignore new file mode 100644 index 000000000..827673668 --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/.dockerignore @@ -0,0 +1,32 @@ +# Development artifacts +target/ +.github/ +.mvn/ +docs/ +.idea/ +storage/ +gatling/ +repl/ +.DS_Store + +# Documentation +_includes/ +docs/ +*.md +LICENSE + +# Git and config files not needed for build +.git/ +.gitignore +.gitattributes +.editorconfig + +# Build scripts not used in Docker (image has its own Maven) +mvnw +mvnw.cmd +Dockerfile + +# Scripts and local dev files +docker-compose.yaml +.dockerignore +_config.yml diff --git a/jdk_25_maven/cs/rest/jasper/.editorconfig b/jdk_25_maven/cs/rest/jasper/.editorconfig new file mode 100644 index 000000000..2f73c929a --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/.editorconfig @@ -0,0 +1,20 @@ + +root = true + +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = tab +indent_size = 2 + +[*.{java,xml,json}] +indent_size = 4 + +[*.{md,yml,yaml,json}] +indent_style = space +trim_trailing_whitespace = false + +[CODEOWNERS] +indent_size = 8 diff --git a/jdk_25_maven/cs/rest/jasper/.gitattributes b/jdk_25_maven/cs/rest/jasper/.gitattributes new file mode 100644 index 000000000..ca61722de --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/.gitattributes @@ -0,0 +1,150 @@ +# This file is inspired by https://github.com/alexkaratarakis/gitattributes +# +# Auto detect text files and perform LF normalization +# http://davidlaing.com/2012/09/19/customise-your-gitattributes-to-become-a-git-ninja/ +* text=auto + +# The above will handle all files NOT found below +# These files are text and should be normalized (Convert crlf => lf) + +*.bat text eol=crlf +*.cmd text eol=crlf +*.ps1 text eol=crlf +*.coffee text +*.css text +*.cql text +*.df text +*.ejs text +*.html text +*.java text +*.js text +*.json text +*.less text +*.properties text +*.sass text +*.scss text +*.sh text eol=lf +*.sql text +*.txt text +*.ts text +*.xml text +*.yaml text +*.yml text + +# Documents +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain +*.markdown text +*.md text +*.adoc text +*.textile text +*.mustache text +*.csv text +*.tab text +*.tsv text +*.txt text +AUTHORS text +CHANGELOG text +CHANGES text +CONTRIBUTING text +COPYING text +copyright text +*COPYRIGHT* text +INSTALL text +license text +LICENSE text +NEWS text +readme text +*README* text +TODO text + +# Graphics +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.tif binary +*.tiff binary +*.ico binary +# SVG treated as an asset (binary) by default. If you want to treat it as text, +# comment-out the following line and uncomment the line after. +*.svg binary +#*.svg text +*.eps binary + +# These files are binary and should be left untouched +# (binary is a macro for -text -diff) +*.class binary +*.jar binary +*.war binary + +## LINTERS +.csslintrc text +.eslintrc text +.jscsrc text +.jshintrc text +.jshintignore text +.stylelintrc text + +## CONFIGS +*.conf text +*.config text +.editorconfig text +.gitattributes text +.gitconfig text +.gitignore text +.htaccess text +*.npmignore text + +## HEROKU +Procfile text +.slugignore text + +## AUDIO +*.kar binary +*.m4a binary +*.mid binary +*.midi binary +*.mp3 binary +*.ogg binary +*.ra binary + +## VIDEO +*.3gpp binary +*.3gp binary +*.as binary +*.asf binary +*.asx binary +*.fla binary +*.flv binary +*.m4v binary +*.mng binary +*.mov binary +*.mp4 binary +*.mpeg binary +*.mpg binary +*.swc binary +*.swf binary +*.webm binary + +## ARCHIVES +*.7z binary +*.gz binary +*.rar binary +*.tar binary +*.zip binary + +## FONTS +*.ttf binary +*.eot binary +*.otf binary +*.woff binary +*.woff2 binary diff --git a/jdk_25_maven/cs/rest/jasper/.gitignore b/jdk_25_maven/cs/rest/jasper/.gitignore new file mode 100644 index 000000000..5c47825ed --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/.gitignore @@ -0,0 +1,35 @@ +HELP.md +target/ +storage/ +repl/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/jdk_25_maven/cs/rest/jasper/.m2/settings.xml b/jdk_25_maven/cs/rest/jasper/.m2/settings.xml new file mode 100644 index 000000000..40c91efda --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/.m2/settings.xml @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/jdk_25_maven/cs/rest/jasper/.mvn/wrapper/maven-wrapper.properties b/jdk_25_maven/cs/rest/jasper/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 000000000..216df0589 --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,3 @@ +wrapperVersion=3.3.4 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.16/apache-maven-3.9.16-bin.zip diff --git a/jdk_25_maven/cs/rest/jasper/Dockerfile b/jdk_25_maven/cs/rest/jasper/Dockerfile new file mode 100644 index 000000000..c264be3c5 --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/Dockerfile @@ -0,0 +1,89 @@ +FROM oven/bun:1.3.14-slim AS bun + +FROM maven:3.9.16-amazoncorretto-25-debian AS builder +WORKDIR /app +COPY pom.xml . +COPY .m2/settings.xml . +RUN mvn -gs settings.xml -B clean package -Dmaven.main.skip -Dmaven.test.skip -Dcodegen.skip && rm -r target +COPY src ./src +RUN mvn -gs settings.xml -B package -Dmaven.test.skip +# Check layers with +# java -Djarmode=tools -jar target/*.jar list-layers +RUN java -Djarmode=tools -jar target/*.jar extract --layers --launcher --destination layers + +FROM builder AS test +COPY docker/entrypoint.sh . +ENV BUN_RUNTIME_TRANSPILER_CACHE_PATH=0 +ENV BUN_INSTALL_BIN=/usr/local/bin +COPY --from=bun /usr/local/bin/bun /usr/local/bin/ +RUN ln -s /usr/local/bin/bun /usr/local/bin/bunx \ + && which bun \ + && which bunx \ + && bun --version +ENV JASPER_NODE=/usr/local/bin/bun +RUN rm /etc/apt/sources.list.d/corretto.list +RUN apt-get update && apt-get install python3 python3-venv python3-pip python3-yaml -y \ + && which python3 \ + && python3 --version +ENV JASPER_PYTHON=/usr/bin/python3 +RUN apt-get update && apt-get install wget bash jq uuid-runtime -y \ + && which jq \ + && jq --version \ + && uuidgen jq \ + && uuidgen --version \ + && which bash \ + && bash --version +ARG JASPER_SHELL=/usr/bin/bash +CMD mvn -gs settings.xml test jacoco:report surefire-report:report; \ + mkdir -p /tests && \ + cp target/surefire-reports/* /tests/ && \ + mkdir -p /reports && \ + cp -r target/reports/* /reports/ && \ + cp target/reports/surefire.html /reports/index.html && \ + mkdir -p /reports/coverage && \ + if [ -d target/site/jacoco ]; then cp -r target/site/jacoco/* /reports/coverage/; fi + +FROM azul/zulu-openjdk-debian:25.0.3-25.34-jre AS deploy +RUN apt-get update && apt-get upgrade -y \ + && apt-get install curl -y \ + && apt-get clean && rm -rf /var/lib/apt/lists/* +ENV BUN_RUNTIME_TRANSPILER_CACHE_PATH=0 +ENV BUN_INSTALL_BIN=/usr/local/bin +COPY --from=bun /usr/local/bin/bun /usr/local/bin/ +RUN ln -s /usr/local/bin/bun /usr/local/bin/bunx \ + && which bun \ + && which bunx \ + && bun --version +ARG JASPER_NODE=/usr/local/bin/bun +ENV JASPER_NODE=${JASPER_NODE} +RUN apt-get update && apt-get install python3 python3-venv python3-pip python3-yaml -y \ + && which python3 \ + && python3 --version \ + && apt-get clean && rm -rf /var/lib/apt/lists/* +ARG JASPER_PYTHON=/usr/bin/python3 +ENV JASPER_PYTHON=${JASPER_PYTHON} +ENV PYTHONUNBUFFERED=1 +RUN apt-get update && apt-get install wget bash jq uuid-runtime -y \ + && which jq \ + && jq --version \ + && uuidgen -r > /dev/null \ + && uuidgen --version \ + && which bash \ + && bash --version \ + && apt-get clean && rm -rf /var/lib/apt/lists/* +ARG JASPER_SHELL=/usr/bin/bash +ENV JASPER_SHELL=${JASPER_SHELL} +RUN apt-get update && apt-get install -y \ + ffmpeg \ + && apt-get clean && rm -rf /var/lib/apt/lists/* +RUN mkdir -p /var/lib/jasper +WORKDIR /app +COPY --from=builder /app/layers/dependencies/ ./ +RUN true +COPY --from=builder /app/layers/spring-boot-loader/ ./ +RUN true +COPY --from=builder /app/layers/snapshot-dependencies/ ./ +RUN true +COPY --from=builder /app/layers/application/ ./ +COPY docker/entrypoint.sh . +ENTRYPOINT ["sh", "entrypoint.sh"] diff --git a/jdk_25_maven/cs/rest/jasper/LICENSE b/jdk_25_maven/cs/rest/jasper/LICENSE new file mode 100644 index 000000000..2c5b3f597 --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Chris Malloy + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/jdk_25_maven/cs/rest/jasper/README.md b/jdk_25_maven/cs/rest/jasper/README.md new file mode 100644 index 000000000..3c1956208 --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/README.md @@ -0,0 +1,1034 @@ +# Jasper +Knowledge Management Server + +[![Build & Test](https://github.com/cjmalloy/jasper/actions/workflows/test.yml/badge.svg)](https://cjmalloy.github.io/jasper/reports/latest-junit/) +[![Coverage](https://img.shields.io/endpoint?url=https://cjmalloy.github.io/jasper/reports/latest-junit/coverage-badge.json)](https://cjmalloy.github.io/jasper/reports/latest-junit/coverage/) +[![Gatling](https://github.com/cjmalloy/jasper/actions/workflows/gatling.yml/badge.svg)](https://cjmalloy.github.io/jasper/reports/latest-gatling/) +[![Dependabot](https://img.shields.io/endpoint?url=https://cjmalloy.github.io/jasper/reports/dependabot-badge.json)](https://github.com/cjmalloy/jasper/security/dependabot) +[![OpenAPI](https://img.shields.io/badge/OpenAPI-1.3.6-brightgreen)](https://editor.swagger.io/?url=https://raw.githubusercontent.com/cjmalloy/jasper/refs/heads/master/src/main/resources/swagger/api.yml) +[![Artifact Hub](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/jasper)](https://artifacthub.io/packages/helm/jasper/jasper) + +## Quickstart +To start the server, client, and database with a single admin user, run +the [quickstart](https://github.com/cjmalloy/jasper-ui/blob/master/quickstart/docker-compose.yaml) +docker compose file. See [Jasper App](https://github.com/cjmalloy/jasper-app) for an installable +electron wrapper. + +See [Jasper-UI](https://github.com/cjmalloy/jasper-ui) for documentation on the reference client. + +## Knowledge Management +Jasper is an open source knowledge management system. It provides a generic set of tools for dealing +with knowledge management style problems. Knowledge management type problems include: +* Business Intelligence +* Scientific Research +* Journalism +* Web Forums +* Wiki (Encyclopedia) +* Task Management +* Libraries +* Customer Support +* Collaborative Writing +* Personal Knowledge Management +* E-mail + +Jasper can be configured to host all these products, individually or in combination. +Run it as an app or a website and connect them together to build a networked system. +Prevent data loss by having full or partial replication of data across the network. +Safely ingest external data sources with one-way replication. +Enforce conformity with a flexible data model, or simply collate unstructured reports. + +### Security +Jasper uses Tag Based Access Control (TBAC) to assign fine grained access controls to any object in the +system. This system is simple and powerful, such that the entire security specification is contained +in a [small, readable file](https://github.com/cjmalloy/jasper/blob/master/src/main/java/jasper/security/Auth.java). + +### Build your own client +Connect to Jasper with a custom client to give users a streamlined user experience. Frontend +developers can create a bespoke interface without needing to make any server side changes. Create custom +plugins and templates and ensure data shape with [JTD](https://jsontypedef.com/docs/jtd-in-5-minutes/) +schemas. Fork [the reference client](https://github.com/cjmalloy/jasper-ui) or use the +[OpenApi docs](https://editor.swagger.io/?url=https://raw.githubusercontent.com/cjmalloy/jasper/refs/heads/master/src/main/resources/swagger/api.yml) to generate API stubs. + +## Standards +Jasper is a standard data model and API. While JSON is used in this document, Jasper may be generalised +to other presentations, such as XML, YAML, or TOML. +Jasper defines five entity types, an access control model, and a plugin/templating system for extending +the model. +1. Ref +2. Ext +3. User +4. Plugin +5. Template + +The main entity is the Ref, it represents a reference to an external resource. The main field in a Ref +is the URL field which can be a link to a web page, or a reference to any arbitrary resources predicated +by the URL scheme. Web content will of course use the http or https scheme. To reference a book, +one could use the [ISBN](https://en.wikipedia.org/wiki/ISBN) scheme (i.e. `isbn:978-3-16-148410-0`). +For comments, [Jasper-UI](https://github.com/cjmalloy/jasper-ui) uses a `comment` scheme followed by an arbitrary ID, usually a UUID +(i.e. `comment:75b36465-4236-4d64-8c78-027d87f3c072`). For hosting internal wikis, +[Jasper-UI](https://github.com/cjmalloy/jasper-ui) uses a `wiki` scheme followed by the +[Wiki Page Name](https://en.wikipedia.org/wiki/Wikipedia:Page_name) (i.e. `wiki:John_Cena`). + +Like the [OSI model](https://en.wikipedia.org/wiki/OSI_model), Jasper's data model is defined in layers: +1. **Identity Layer** - persistence of individual entities +2. **Indexing Layer** - query and transport of entities +3. **Application Layer** - custom modifications + +## Tagging +Jasper support hierarchical tagging of Refs. Tags are not entities, they are strings with +regex `[_+]?[a-z0-9]+([./][a-z0-9]+)*`. Tags are part of the primary key for Tag-like entities, but no +entities need exist to use a tag. +Refs have a list of tags which can be used for categorization, permissions, and plugins. +There are three types of tags, which the type defined as a semantic ontology: +`public`, `+protected`, `_private` tags. The character prefix defines the type while also being +part of the tag itself. Therefore, no lookup is ever required to determine the tag type. + * A public tag can be used freely by anyone. This includes tagging a Ref, or using it in a query. + * A protected tag can freely be used in a query, but you cannot tag a Ref with a protected tag +unless it is in your [read access](#access-control) list. + * A private tag cannot be used at all unless permission is given. When fetching a Ref that includes +private tags, they will be removed by the server prior to sending. See +[access control](#access-control) for more. + +Tags may also be fully qualified by appending the origin. (i.e. `tag@origin`). +Use forward slashes to define hierarchical tags (i.e. `people/murray/bill` or `people/murray/anne`) + +## Querying +When fetching a page or Refs a query may be specified. The query language uses simple set-like +operators to match Refs according to their tag list and Origin. You may use tags, origins, or +fully qualified tags (tag + origin). There is a special origin `@` which will match the +default origin `""` (the empty string). +If a tag is not fully qualified it will match the wildcard origin `"@*"`. The `*` +wild card can be used to match anything on the default origin `""` (empty string). +Valid operators in a query are: +1. `:` and +2. `|` or +3. `!` not +4. `()` groups + +Note: In the current implementation, groups may not be nested. + +Example queries: + * `science`: All Refs that include the `science` tag + * `science|funny`: All Refs that have either the `science` tag or the `funny` tag + * `science:funny`: All Refs that have both the `science` tag and the `funny` tag + * `science:!funny`: All Refs that have the `science` tag but do not have the `funny` tag + * `(science|math):funny`: All Refs that have either the `science` or `math` tags, but +also the `funny` tag. This would match a ref with `['science', 'funny']`, `['math', 'funny']`, +but would not match `['science', 'math']` + * `science:funny|math:funny`: Extended form of previous query. Would produce the exact same results. + * `music:people/murray`: All Refs that have the `music` tag and `people/murray` tag. It would also +match Refs with `['music', 'people/murray/anne']` or `['music', 'people/murray/bill']` + +## Sorting +Jasper supports dynamic sorting on fields using arrow notation (`->`). This allows sorting by +any field within the `plugins`, `metadata`, `config`, or `external` JSONB columns without requiring +database schema changes. + +**Sort Syntax:** +- Use `->` as the path separator to navigate fields +- Append `:num` suffix for numeric sorting (otherwise values sort as strings) +- Append `:len` suffix to sort by array length, origin nesting level, or tag levels +- Use `[index]` notation for array element access (e.g., `external->ids[0]`) + +The `:num` and `:len` suffixes are automatically applied to metadata fields, so you can use +`metadata->responses` instead of `metadata->responses:len`. + +## Modding +Jasper allows extensive modification with server reuse. Since changes are done by creating +Plugin and Template entities, server restarts are not required. +This method of modding means that only client changes are required. The same Jasper server, +without any code modifications, can be used. The client can define and support its own Plugins +and Templates. This allows for much more flexible development, as writing client code (in particular +web clients) is much easier than writing server code. A developer with only front-end expertise +can extend the Jasper model to support arbitrary applications. +In order to extend the functionality of a Ref, a developer may choose a set of tags or URL scheme +and a convention by which they modify the semantics of a Ref. If a custom data model is also +required, a Plugin entity may be created which defines a +[JTD](https://jsontypedef.com/docs/jtd-in-5-minutes/) schema. A Plugin is a Tag-like entity. When +a Ref is tagged with a Plugin, the Plugin may be considered active for that Ref. The Ref may then +store data in its config field and the server will validate it according to the schema. +Similarly, Ext entities may be created which extend the functionality of a tag. As Plugins define +custom data that can be stored in a ref, Templates may be created which allow custom data to be +stored in Ext entities and similarly validated according to their schema. + +See [Jasper-UI](https://github.com/cjmalloy/jasper-ui) for examples of Plugins and Templates, such as: +* `plugin/thumbnail`: [This plugin](https://github.com/cjmalloy/jasper-ui/blob/master/src/app/mods/thumbnail.ts) +allows a Ref to include a URL to a thumbnail image. +* `user` Template: +[This template](https://github.com/cjmalloy/jasper-ui/blob/master/src/app/mods/user.ts) +allows a user tag to customize their experience, such as subscribing to a list of tags to show +on their home page. + +## Entities +There are two types of entities in Jasper: +1. Refs +2. Tags (including Exts, Plugins, Templates, and Users) + +![entities](./docs/entities.png) +Origins are used to facilitate replication and multi-tenant operation. Each origin represents a +jasper instance that that entity originated from. +![origins](./docs/origins.png) + +### Ref +Refs are the main data model in Jasper. A Ref defines a URL to a remote resource. Example: +```json +{ + "url": "https://www.youtube.com/watch?v=9Gn4rmQTZek", + "origin": "", + "title": "Why does Science News Suck So Much?", + "comment": "Sabine Hossenfelder", + "tags": ["public", "youtube", "sabine"], + "sources": [], + "alternateUrls": [], + "plugins": { + "plugin/thumbnail": {"url": "https://...jpg"} + }, + "metadata": { + "responses": 0, + "internalResponses": 0, + "plugins": {}, + "modified": "2022-06-18T12:07:04.404272Z" + }, + "published": "2022-06-18T12:00:07Z", + "created": "2022-06-18T12:07:04.404272Z", + "modified": "2022-06-18T12:07:04.404272Z" +} +``` +Only the "url" field is required. + +The combination of URL and Origin for a Ref must be unique and may be used as a Primary Composite Key. +Implementations may also make the modified date part of the composite primary key for version history. + +**URL:** The url of the resource. +**Origin:** The Origin this Ref was replicated from, or the empty string for local. +**Title:** Optional title for this Ref. +**Comment:** Optional comment for this Ref, usually markdown. +**Tags:** A list of tags used to categorise this Ref. All tags must match the regex `[_+]?[a-z0-9]+([./][a-z0-9]+)*` +**Sources:** A list of URLs which are sources for this Ref. These may or may not have a corresponding Ref +entity. If a source URL does correspond to a Ref, the published date of the source must predate the +published date of this Ref. +**Alternate URLs:** Alternate URLs which should be considered synonymous with the URL of this Ref. This +should be used as part of a uniqueness check when ingesting Refs. +**Plugins:** A JSON object with plugin tags as fields and arbitrary JSON data defined by each respective +plugin. Must be valid according to each plugin's schema. +**Metadata:** Optional data generated by the server for this resource. Includes response links (inverse +source lookup). +**Published:** The published date of this resource. Default to create date if not known. This date must +be later than the published date of all sources. +**Created:** Created date of this Ref. +**Modified:** Last modified date of this Ref. If this is the same as the created date no modification +has occurred. Does not update if Metadata is modified. + +### Ext +An Ext is a Tag-like entity representing a Tag extension. +```json +{ + "tag": "news", + "origin": "", + "name": "News", + "config": { + "pinned":[], + "sidebar": "" + }, + "modified": "2022-06-18T16:00:59.978700Z" +} +``` +Only the "tag" field is required. + +An Ext allows you to customise a Tag page. For example, you could set the sidebar text or pin some links. + +The combination of Tag and Origin for a Ext must be unique and may be used as a Primary Composite Key. +Implementations may also make the modified date part of the composite primary key for version history. + +**Tag:** The tag of this Ext. Must match the regex `[_+]?[a-z0-9]+([./][a-z0-9]+)*` +**Origin:** The Origin this Ext was replicated from, or the empty string for local. +**Name:** The display name of this Ext. Used to customise the page title for the Tag page. +**Config:** Arbitrary JSON data defined by Templates. Must be valid according to each template's schema. +**Modified:** Last modified date of this Ext + +### User +A User is a Tag-like entity representing a user. + +```json +{ + "tag": "+user/charlie", + "origin": "", + "name": "Charlie Brown", + "readAccess": [], + "writeAccess": [], + "tagReadAccess": [], + "tagWriteAccess": [], + "pubKey": "...", + "external": { + "ids": [] + }, + "modified": "2022-06-18T16:00:59.978700Z" +} +``` +Only the "tag" field is required. + +A User contains the access control information for the system. Access tags work in all +sub-origins. + +The combination of Tag and Origin for a User must be unique and may be used as a Primary Composite Key. +Implementations may also make the modified date part of the composite primary key for version history. + +**Tag:** The tag of this User. Must match the regex `[_+]user/[a-z0-9]+([./][a-z0-9]+)*` +**Origin:** The Origin this User was replicated from, or the empty string for local. +**Name:** The display name of this User. Used to customise the page title for the Tag page. +**Read Access:** List of tags this user has complete read access to. Grants read access to all +entities with this tag. +**Write Access:** List of tags this user has complete write access to. Grants write access to +all entities with this tag. +**Tag Read Access:** List of tags this user can read. Only applies to Tag-like entities. Only needed +for private tags. +**Tag Write Access:** List of tags this user can write. Only applies to Tag-like entities. +**External IDs:** IDs used in an external auth system. Only used when external IDs are enabled. +**Pub Key:** Base 64 encoded public RSA key. Used for verifying signatures to validate authorship. +**Modified:** Last modified date of this User. + +### Plugin +A Plugin is a Tag-like entity used to extend the functionality of Refs. +```json +{ + "tag": "plugin/thumbnail", + "origin": "", + "name": "⭕️ Thumbnail", + "config": {...}, + "defaults": {}, + "schema": { + "optionalProperties": { + "url": {"type": "string"}, + "width": {"type": "int32", "nullable": true}, + "height": {"type": "int32", "nullable": true} + } + }, + "modified": "2022-06-18T16:27:13.774959Z" +} +``` +Only the "tag" field is required. + +Tagging a ref with a Plugin tag applies that plugin to the Ref. The Ref plugin must contain valid +data according to the Plugin schema. + +The combination of Tag and Origin for a Plugin must be unique and may be used as a Primary Composite Key. +Implementations may also make the modified date part of the composite primary key for version history. + +**Tag:** The tag of this Plugin. Must match the regex `[_+]?plugin/[a-z0-9]+([./][a-z0-9]+)*` +**Origin:** The Origin this Plugin was replicated from, or the empty string for local. +**Name:** The display name of this Ext. Used to customise the page title for the Tag page. +**Config:** Arbitrary JSON. +**Defaults:** Default plugin data if creating a new Ref with empty plugin data. May be any JSON value (object, array, or scalar). +**Schema:** Json Type Def (JTD) schema used to validate plugin data in Ref. +**Modified:** Last modified date of this Plugin. + +### Template +A Template is a Tag-like entity used to extend the functionality of Exts. +```json +{ + "tag": "", + "origin": "", + "name": "Default Template", + "config": {...}, + "defaults": { + "pinned": [] + }, + "schema": { + "properties": { + "pinned": {"elements": {"type": "string"}} + }, + "optionalProperties": { + "sidebar": {"type": "string"} + } + }, + "modified": "2022-06-18T16:27:13.774959Z" +} +``` +Only the "tag" field is required (can be the empty string). + +The Tag in the case of a template is actually a Tag prefix. This Template matches all Exts +where its tag followed by a forward slash is a prefix of the Ext tag. In the case of the empty +string the Template matches all Exts. + +The combination of Tag and Origin for this Template must be unique and may be used as a Primary Composite Key. +Implementations may also make the modified date part of the composite primary key for version history. + +**Tag:** The tag of this Template. Must match the regex `[_+]?[a-z0-9]+([./][a-z0-9]+)*` or the empty string. +**Origin:** The Origin this Template was replicated from, or the empty string for local. +**Name:** The display name of this Template. +**Config:** Arbitrary JSON. +**Defaults:** Default Ext config if creating a new Ext with empty config. +**Schema:** Json Type Def (JTD) schema used to validate Ext config. +**Modified:** Last modified date of this Template. + +## Layers +The jasper model is defined in layers. This is to facilitate lower level operations such as routing, querying, +and archiving. + +### Identity Layer +The identity layer of the Jasper model defines how entities are stored or retrieved. A system operating +at this layer should be extremely lenient when validating entities. Only the identity fields of the +entity need to be considered. The identity fields are: +1. Refs: (URL, Origin, Modified) +2. Tags: (Tag, Origin, Modified) + +Together, the (Origin, Modified) keys represent the cursor of the entity, which is used in origin based +replication. + +### Indexing Layer +The indexing layer of the Jasper model adds tags to Refs. A system operating at this layer should support +tag queries, sorting, and filtering. + +### Validation Layer +The validation layer of the Jasper model includes all entity fields. Plugins and Templates are validated +according to their schema. + +#### Plugin and Template Inheritance +Plugins and Templates behave differently in how they inherit the fields of the parent Ext. +Plugins stack and templates merge. +For example, the Plugin `plugin/test` like: +```json +{ + "tag": "plugin/test", + "schema": { + "properties": { + "test": { "type": "string" } + } + } +} +``` +And the Plugin `plugin/test/this` like: +```json +{ + "tag": "plugin/test/this", + "schema": { + "properties": { + "more": { "type": "string" } + } + } +} +``` +If we use both of these plugins in the same Ref, both plugins would have their +data stacked, like: +```json +{ + "url": "test:1", + "plugins": { + "plugin/test": { + "test": "data" + }, + "plugin/test/this": { + "more": "tests" + } + } +} +``` +A template would merge all fields, overwriting at every stage, into a final result. +For example, the Template `a` like: +```json +{ + "tag": "a", + "schema": { + "properties": { + "test": { "type": "string" } + } + } +} +``` +And the Template `a/b` like: +```json +{ + "tag": "a/b", + "schema": { + "properties": { + "more": { "type": "string" } + } + } +} +``` +If we use both of these plugins in the same Ext, both plugins would have their +data merged, like: +```json +{ + "tag": "a/b/c", + "config": { + "test": "data", + "more": "tests" + } +} +``` +If a child Template defines an overlapping field in the schema, it will override the parent type. + +### Modding Layer +The modding layer of the Jasper model is entirely client side. No server changes are required in order to +support new plugins or templates. + +## Cursor Replication +Distributed systems must make tradeoffs according to the [CAP theorem](https://en.wikipedia.org/wiki/CAP_theorem). +According to the CAP theorem you may only provide two of these three guarantees: consistency, availability, +and partition tolerance. Jasper uses an eventually consistent model, where availability and partition +tolerance are guaranteed. The modified date is used as a cursor to efficiently poll for modified records. + +To replicate a Jasper instance simply create a Ref for that instance and tag it `+plugin/origin/pull`. +Add the `+plugin/cron` tag to schedule pulling, or add the `+plugin/user/run` response tag to pull a +single time. + +The modified date of the last entity received will be stored and used for the next poll. When polling, +the Jasper server requests a batch of entities from the remote instance where the modified date is +after the last stored modified date, sorted by modified date ascending. + +### Duplicate Modified Date +Jasper instances should enforce unique modified dates as the cursor for each entity type. Otherwise, +when receiving +a batch of entities, it's possible that the last entity you received has a modified date that is +exactly the same as another entity. If that is the case, requesting the next batch after that modified +date will skip such entities. + +To prevent duplicate modified dates it's enough to add a single millisecond to the date until it +is unique. + +## Deployment +Jasper is available in the following distributions: + - [Docker image](https://github.com/cjmalloy/jasper/pkgs/container/jasper) + - [Helm chart](https://artifacthub.io/packages/helm/jasper/jasper-ui) + - [Jar](https://github.com/cjmalloy/jasper/releases/latest) + +It supports the following configuration options: + +| Environment Variable | Description | Default Value (in prod) | +|-----------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `SERVER_PORT` | Port to listen for HTTP connections. | `8081` | +| `SPRING_PROFILES_ACTIVE` | Set the comma separated list of runtime profiles. | `default` | +| `SPRING_DATASOURCE_URL` | PostgreSQL database connection string. | `jdbc:postgresql://localhost:5432/jasper` | +| `SPRING_DATASOURCE_USERNAME` | PostgreSQL database username. | `jasper` | +| `SPRING_DATASOURCE_PASSWORD` | PostgreSQL database password. | | +| `JASPER_DEBUG` | | `false` | +| `JASPER_LOCAL_ORIGIN` | The origin of this server. The local origin may be set to a sub origin via the `Local-Origin` header. | `""` | +| `JASPER_WORKLOAD` | List of sub-origin sandboxes for worker nodes. | | +| `JASPER_WORKER` | ID of the worker. Must end in a number which is used to index into JASPER_WORKLOAD to set the worker origin. | | +| `JASPER_ALLOW_USER_TAG_HEADER` | Allow pre-authentication of a user via the `User-Tag` header. | `false` | +| `JASPER_ALLOW_USER_ROLE_HEADER` | Allows escalating user role via `User-Role` header. | `false` | +| `JASPER_ALLOW_AUTH_HEADERS` | Allow adding additional user permissions via `Read-Access`, `Write-Access`, `Tag-Read-Access`, and `Tag-Write-Access` headers. | `false` | +| `JASPER_MAX_ROLE` | Highest role allowed to access the server. Users with a higher role will have their role reduced to this. | `ROLE_ADMIN` | +| `JASPER_MIN_ROLE` | Minimum role required to access the server. | `ROLE_ANONYMOUS` | +| `JASPER_MIN_WRITE_ROLE` | Minimum role required to write to the server. | `ROLE_VIEWER` | +| `JASPER_MIN_FETCH_ROLE` | Minimum role required to fetch external resources. | `ROLE_USER` | +| `JASPER_MIN_CONFIG_ROLE` | Minimum role required to edit plugins and templates. | `ROLE_ADMIN` | +| `JASPER_MIN_READ_BACKUPS_ROLE` | Minimum role required to download backups. | `ROLE_ADMIN` | +| `JASPER_DEFAULT_ROLE` | Default role given to all users. | `ROLE_ANONYMOUS` | +| `JASPER_DEFAULT_READ_ACCESS` | Additional read access qualified tags to apply to all users. | | +| `JASPER_DEFAULT_WRITE_ACCESS` | Additional write access qualified tags to apply to all users. | | +| `JASPER_DEFAULT_TAG_READ_ACCESS` | Additional tag read access qualified tags to apply to all users. | | +| `JASPER_DEFAULT_TAG_WRITE_ACCESS` | Additional tag write access qualified tags to apply to all users. | | +| `JASPER_INGEST_MAX_RETRY` | Maximum number of retry attempts for getting a unique modified date when ingesting a Ref. | `5` | +| `JASPER_BACKUP_BUFFER_SIZE` | Size of buffer in bytes used to cache JSON in RAM before flushing to disk during backup. | `1000000` | +| `JASPER_RESTORE_BATCH_SIZE` | Number of entities to restore in each transaction. | `500` | +| `JASPER_BACKFILL_BATCH_SIZE` | Number of entities to generate Metadata for in each transaction when backfilling. | `100` | +| `JASPER_CLEAR_CACHE_COOLDOWN_SEC` | Number of seconds to throttle clearing the config cache. | `2` | +| `JASPER_PUSH_COOLDOWN_SEC` | Number of seconds to throttle pushing after modification. | `1` | +| `JASPER_STORAGE` | Path to the folder to use for storage. Used by the backup system. | `/var/lib/jasper` | +| `JASPER_NODE` | Path to node binary for running javascript deltas. | `/usr/local/bin/node` | +| `JASPER_PYTHON` | Path to python binary for running python scripts. | `/usr/bin/python` | +| `JASPER_SHELL` | Path to shell binary for running shell scripts. | `/usr/bin/bash` | +| `JASPER_CACHE_API` | HTTP address of an instance where storage is enabled. | | +| `JASPER_SSH_CONFIG_NAMESPACE` | K8s namespace to write authorized_keys config map file to. | `default` | +| `JASPER_SSH_CONFIG_MAP_NAME` | K8s config map name to write `authorized_keys` file to. | `ssh-authorized-keys` | +| `JASPER_SSH_SECRET_NAME` | K8s secret name to write the `host_key` file to. | `ssh-host-key` | +| `JASPER_SECURITY_CONTENT_SECURITY_POLICY` | Set the CSP header. | `"default-src 'self'; frame-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://storage.googleapis.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:"` | +| `JASPER_OVERRIDE_SERVER_EMAIL_HOST` | Override the server email host. | | +| `JASPER_OVERRIDE_SERVER_MAX_SOURCES` | Override the server max sources. | | +| `JASPER_OVERRIDE_SERVER_MOD_SEALS` | Override the server mod seals. | | +| `JASPER_OVERRIDE_SERVER_EDITOR_SEALS` | Override the server editor seals. | | +| `JASPER_OVERRIDE_SERVER_WEB_ORIGINS` | Override the server origins with web access. | | +| `JASPER_OVERRIDE_SERVER_MAX_REPL_ENTITY_BATCH` | Override the server maximum batch size for replicate controller. | | +| `JASPER_OVERRIDE_SERVER_SSH_ORIGINS` | Override the server origins with SSH access. | | +| `JASPER_OVERRIDE_SERVER_MAX_PUSH_ENTITY_BATCH` | Override the server maximum batch size for push replicate. | | +| `JASPER_OVERRIDE_SERVER_MAX_PULL_ENTITY_BATCH` | Override the server maximum batch size for pull replicate. | | +| `JASPER_OVERRIDE_SERVER_SCRIPT_SELECTORS` | Override the server tags and origins that can run scripts. No wildcard origins. | | +| `JASPER_OVERRIDE_SERVER_SCRIPT_WHITELIST` | Override the server list of whitelisted script SHA-256 hashes. | | +| `JASPER_OVERRIDE_SERVER_HOST_WHITELIST` | Override the server list of whitelisted hosts. | | +| `JASPER_OVERRIDE_SERVER_HOST_BLACKLIST` | Override the server list of blacklisted hosts. | | +| `JASPER_OVERRIDE_SERVER_MAX_REQUESTS` | Override the server maximum HTTP requests per origin every 500 nanoseconds. | `50` | +| `JASPER_OVERRIDE_SERVER_MAX_CONCURRENT_REQUESTS` | Override the server global maximum concurrent HTTP requests (across all origins). | `500` | +| `JASPER_OVERRIDE_SERVER_MAX_CONCURRENT_SCRIPTS` | Override the server maximum concurrent script executions. | `100_000` | +| `JASPER_OVERRIDE_SERVER_MAX_CONCURRENT_REPLICATION` | Override the server maximum concurrent replication push/pull operations. | `3` | +| `JASPER_OVERRIDE_SERVER_MAX_CONCURRENT_FETCH` | Override the server maximum concurrent fetch operations (scraping). | `10` | +| `JASPER_OVERRIDE_SECURITY_MODE` | Override the security mode for all origins. | | +| `JASPER_OVERRIDE_SECURITY_CLIENT_ID` | Override the security clientId for all origins. | | +| `JASPER_OVERRIDE_SECURITY_BASE64_SECRET` | Override the security base64Secret for all origins. | | +| `JASPER_OVERRIDE_SECURITY_SECRET` | Override the security secret for all origins. | | +| `JASPER_OVERRIDE_SECURITY_JWKS_URI` | Override the security jwksUri for all origins. | | +| `JASPER_OVERRIDE_SECURITY_USERNAME_CLAIM` | Override the security usernameClaim for all origins. | | +| `JASPER_OVERRIDE_SECURITY_VERIFIED_EMAIL_CLAIM` | Override the security verifiedEmailClaim for all origins. | `unset` | +| `JASPER_OVERRIDE_SECURITY_DEFAULT_USER` | Override the security defaultUser for all origins. | | +| `JASPER_OVERRIDE_SECURITY_TOKEN_ENDPOINT` | Override the security tokenEndpoint for all origins. | | +| `JASPER_OVERRIDE_SECURITY_SCIM_ENDPOINT` | Override the security scimEndpoint for all origins. | | +| `JASPER_OVERRIDE_SECURITY_MAX_REQUESTS` | Override the security maximum HTTP requests per origin every 500 nanoseconds for all origins. | `50` | +| `JASPER_OVERRIDE_SECURITY_MAX_CONCURRENT_SCRIPTS` | Override the security maximum concurrent script executions per origin for all origins. | `5` | +| `JASPER_CORES` | Set the number of processors available to the JVM. Only applies to the docker container. | | +| `JASPER_GC` | Select the JVM garbage collector (`g1gc`, `parallel`, or `zgc`). Only applies to the docker container. | `g1gc` | +| `JASPER_HEAP` | Set both max and initial heap size for the JVM. Only applies to the docker container. | `512m` | + +### Configuration Templates +Jasper uses special templates in the root origin to configure server-wide and per-origin settings. +These templates are automatically generated with default values if they do not exist. + +#### Server Config (`_config/server` Template) +The `_config/server` template is installed in the root origin (the local origin for this server) +and controls server-wide settings. It is automatically created on startup if it does not exist. + +If the current node is running as a worker in origin @worker, the config used will be +`_config/server/worker` in the local origin (not the worker origin). This allows you to assign +different nodes to run different workloads. + +| Field | Description | Default Value | +|----------------------------|-------------------------------------------------------------------------------------------------|--------------------------------------------| +| `emailHost` | Email host used for sending emails. | `jasper.local` | +| `maxSources` | Maximum number of sources allowed per Ref. | `1000` | +| `modSeals` | List of tags that act as mod seals (protected tags that only mods can add/remove). | `["seal", "+seal", "_seal", "_moderated"]` | +| `editorSeals` | List of tags that act as editor seals. | `["plugin/qc"]` | +| `webOrigins` | Whitelist of origins allowed web access. Supports wildcards. | `[""]` (root origin only) | +| `maxReplEntityBatch` | Maximum batch size for the replicate controller. | `500` | +| `sshOrigins` | Whitelist of origins allowed to open SSH tunnels. | `[""]` (root origin only) | +| `maxPushEntityBatch` | Maximum batch size for push replication. | `5000` | +| `maxPullEntityBatch` | Maximum batch size for pull replication. | `5000` | +| `scriptSelectors` | Whitelist of selectors (tag + origin) allowed to run scripts. No origin wildcards. | `[""]` (root origin only) | +| `scriptWhitelist` | Whitelist of script SHA-256 hashes allowed to run. If empty, all scripts are allowed. | `null` (all allowed) | +| `hostWhitelist` | Whitelist of domains allowed to fetch from. If empty, all hosts are allowed (except blacklist). | `null` (all allowed) | +| `hostBlacklist` | Blacklist of domains not allowed to fetch from. Takes precedence over whitelist. | `["*.local"]` | +| `maxConcurrentScripts` | Maximum concurrent script executions server-wide. | `100_000` | +| `maxConcurrentReplication` | Maximum concurrent replication push/pull operations. | `3` | +| `maxRequests` | Maximum HTTP requests per origin every 500 nanoseconds. | `50` | +| `maxConcurrentRequests` | Global maximum concurrent HTTP requests across all origins. | `500` | +| `maxConcurrentFetch` | Maximum concurrent fetch operations (scraping). | `10` | + +#### Security Config (`_config/security` Template) +The `_config/security` template is installed per-origin to configure authentication and authorization +settings. Each tenant can have their own security configuration. It is automatically used with default +values if it does not exist. Security settings are inherited from parent origins if not set. + +| Field | Description | Default Value | +|--------------------------|--------------------------------------------------------------------------------------------------|-------------------------------------------| +| `mode` | Authentication mode (`jwt` or `jwks`). | `""` (none) | +| `clientId` | Client ID for OAuth2/JWT authentication. | `""` | +| `base64Secret` | Base64 encoded secret for JWT validation. | `""` | +| `secret` | Plain text secret for JWT validation (alternative to base64Secret). | `""` | +| `jwksUri` | URI to JWKS endpoint for token validation. | `""` | +| `tokenEndpoint` | OAuth2 token endpoint. | `""` | +| `scimEndpoint` | SCIM endpoint for user management. | `""` | +| `usernameClaim` | JWT claim to use as the username. | `sub` | +| `externalId` | Enable external ID matching for users. | `false` | +| `emailDomainInUsername` | Include email domain in username. | `false` | +| `rootEmailDomain` | Root email domain for the server. | `""` | +| `verifiedEmailClaim` | JWT claim for verified email status. | `verified_email` | +| `authoritiesClaim` | JWT claim for user authorities/roles. | `auth` | +| `readAccessClaim` | JWT claim for read access tags. | `readAccess` | +| `writeAccessClaim` | JWT claim for write access tags. | `writeAccess` | +| `tagReadAccessClaim` | JWT claim for tag read access. | `tagReadAccess` | +| `tagWriteAccessClaim` | JWT claim for tag write access. | `tagWriteAccess` | +| `minRole` | Minimum role for basic access. | `ROLE_ANONYMOUS` | +| `minWriteRole` | Minimum role for writing. | `ROLE_VIEWER` | +| `minFetchRole` | Minimum role for fetching external resources. | `ROLE_USER` | +| `minConfigRole` | Minimum role for admin configuration. | `ROLE_ADMIN` | +| `minReadBackupsRole` | Minimum role for downloading backups. | `ROLE_ADMIN` | +| `defaultRole` | Default role given to every user. | `ROLE_ANONYMOUS` | +| `defaultUser` | Default user tag given to logged out users. | `""` | +| `defaultReadAccess` | Default read access tags for all users. | `null` | +| `defaultWriteAccess` | Default write access tags for all users. | `null` | +| `defaultTagReadAccess` | Default tag read access tags for all users. | `null` | +| `defaultTagWriteAccess` | Default tag write access tags for all users. | `null` | +| `maxRequests` | Maximum HTTP requests per origin every 500 nanoseconds for this origin. | `50` | +| `maxConcurrentScripts` | Maximum concurrent script executions per origin. | `5` | +| `scriptLimits` | Per-origin script execution limits. Map of selector patterns to max concurrent value. | `{}` (empty) | + +### Profiles +Setting the active profiles is done through the `SPRING_PROFILES_ACTIVE` environment +variable. Multiple profiles can be activated by adding them all as a comma +separated list. + +For production use the `prod` profile should be active. For testing, the `dev` profile will +enable additional logging. + +To enable JWT Token Authentication activate the `jwt` profile. +Either set the `_config/security` template in the Origin receiving traffic: +```json +{ + "mode": "jwt", + "clientId": "", + "base64Secret": "", + "secret": "", + "jwksUri": "", + "usernameClaim": "", + "tokenEndpoint": "", + "scimEndpoint": "" +} +``` +or set the environment variable overrides: +- Set `JASPER_OVERRIDE_SECURITY_MODE` to either `jwt` or `jwks` +- Set `JASPER_OVERRIDE_SECURITY_CLIENT_ID` +- For `jwt` set `JASPER_OVERRIDE_SECURITY_BASE64_SECRET` +- For `jwks` set `JASPER_OVERRIDE_SECURITY_JWKS_URI` + +If your user management server supports SCIM, you can enable the `scim` profile to manage users. +Requires the `_config/security` clientId, secret, and scimEndpoint set. Or +`JASPER_OVERRIDE_SECURITY_CLIENT_ID`, `JASPER_OVERRIDE_SECURITY_BASE64_SECRET`, +and `JASPER_OVERRIDE_SECURITY_SCIM_ENDPOINT` +environment variable. + +The `storage` profile is required for backups, caches, or preloading static files. Use the `JASPER_STORAGE` environment +variable to change the location of the storage folder. + +The `preload` profile lets you preload static files. Zip files in the preload folder +`$JASPER_STORAGE/default/preload`. If `$JASPER_LOCAL_ORIGIN` is set, +`$JASPER_STORAGE/$JASPER_LOCAL_ORIGIN/preload` is used. + +The `scripts` profile enables server side scripting through the `plugin/delta` Plugin. + +## Access Control +Jasper uses a combination of simple roles and Tag Based Access Control (TBAC). There are five +hierarchical roles which cover broad access control, Admin, Mod, Editor, User, and Viewer. The +Anonymous role is given to users who are not logged in. +Roles are hierarchical, so they include any permissions granted to a preceding role. + * `ROLE_ANONYMOUS`: read access to public tags and Refs. + * `ROLE_VIEWER`: logged in user. Can be given access to private tags and Refs. + * `ROLE_USER`: can post refs. Has read/write access to their user tag. + * `ROLE_EDITOR`: can add/remove public tags to any post they have read access to. + * `ROLE_MOD`: can read/write any tag or ref except plugins and templates. + * `ROLE_ADMIN`: complete access to origin and sub-origins. Root admin can access all origins Can read/write plugins +and templates, perform backups and restores. + +Tags are used to provide fine-grained access to resources. For Refs, the list of tags are considered. +For Tags entities, their tag is considered. + +The tag permissions are stored in the User entities: + * Tag Read Access + * Can read tag + * Can add tag + * Tag Write Access + * Can edit tag Ext + * Read Access (Refs and Tags) + * Can read ref with tag + * Can read tag + * Can add tag + * Write Access (Refs and Tags) + * No public tags + * Can write ref with tag + * Can edit tag Ext + +The protected and private versions of the User entity are merged when +calculating the tag access lists. +If external IDs are enabled, all matching users by external ID are also merged. + +### Special URL Schemas + +### Cache +URLs that have the `cache:` scheme represent items stored in a file cache. +Most URLs with a resource in a file cache have a standard `https:` scheme, +as they are just a cache of a resource that exists elsewhere. +When a file is pushed into the cache (such as a generated thumbnail), it is +generated a random `cache:` URL. + +#### Tag URLs +URLs that point to a tag, such as `tag:/history` ignore regular tagging access rules. +Instead, you can access this Ref if you can access the tag it points to. + +#### User URLs +URLs that point to a user tag, such as `tag:/+user/chris` are always owned by the user. +These specials URLs can also be used to store per-plugin config data, +such as `tag:/+user/chris?url=tag:/kanban`. +Visibility of plugin setting can be set on a per-user, per-plugin basis. +For convenience, the user URL is used if a blank URL is passed to the tagging response controller. +This allows you to quickly ensure settings are initialized and fetch / edit Ref plugins and tags to read settings. +If a tag are passed, for example `kanban`, the default is the kanban user settings Ref: `tag:/+user/chris?url=tag:/kanban`. +If a blank URL and a blank tag are passed, the default is the generic user settings Ref: `tag:/+user/chris`. +User plugins, which follow the template `plugin/user`, may **only** be added to user URL Refs. + +### Special Tags +Some public tags have special significance: + * `public`: everyone can read + * `internal`: don't show in UI normally, count separately in metadata + * `locked`: No edits allowed (tagging is allowed, but not removing plugin data) + +### Multi-tenant +Users only have read-access to their own origin and sub-origins. +For example, if a tenant has origin `@test`, they can also read `@test.other`. As usual, writing to +origins other than your own is never allowed. + +### Access Tokens +When running the system with JWT authentication, roles may be added as claims. +For example: +```json +{ + "sub": "username", + "auth": "ROLE_USER" +} +``` + +Note: The claim names may be changed with the `JASPER_USERNAME_CLAIM` +and `JASPER_AUTHORITIES_CLAIM` properties. + +## Backup / Restore +Jasper has a built-in backup system for mods and/or admins. Regular users should instead replicate to a separate jasper instance. +In order to use the backup system, the `storage` profile must be active. + +## Validation +When ingesting entities, Jasper performs the following validation: + * Fields must not exceed their maximum length + * URLS are valid according to the regex `(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))` + * Tags are valid according to their respective prefix and the general tag regex `[_+]?[a-z0-9]+([./][a-z0-9]+)*` + * If a Ref has plugins present, any plugin data must conform to the plugin's schema + * If an Ext matches a template prefix, any config must conform to all matching templates merged schemas + +Plugin and Template schemas are in JTD, which only validates the shape of the data. In addition +to total bytes of the entity, these are the only server side data validations performed. No security related +validations should be required on Ref or Ext data, so client side validation should be sufficient in most cases. +Error checking should be the first part of any script parsing user input as part of a workflow. +We always want to err on the side of accepting well-shaped data rather than rejecting it, as server validation +errors rejecting valid user input are infuriating and very common. Error correction can happen as a follow-up step +if the client validation was somehow circumvented. + +## Metadata +Jasper uses metadata generation to pre-compute graph connections. This allows us to store derived data outside +of the main data model and keeps our queries join free. + +Jasper generates the following metadata in Refs: + * List of responses: This is an inverse lookup of the Ref sources. Excludes any Refs with the internal tag. + * List of internal responses: This is an inverse lookup of the Ref sources that include the internal tag. + * List of plugin responses: A list of responses with that plugin. + * List of user plugin responses: A list of responses with that plugin. + * Obsolete: flag set if another origin contains the newest version of this Ref + +Metadata is never transferred during replication. A simplified version is sent over the client API, with +counts for each response type, and user plugin responses for the current user. + +## Server Scripting +When the `scripts` profile is active, scripts may be attached to Refs with either the `plugin/delta` tag or the +`plugin/script` tag. +Only admin users may install scripts and they run with very few guardrails. A regular user may invoke the script +by tagging a Ref. The tagged ref will be serialized as UTF-8 JSON and passed to stdin. Environment variables will +include the API endpoint as `JASPER_API`. Return a non-zero error code to fail the script and attach an error log. +The script should by writing UTF-8 JSON to stdout of the form: + +```json +{ + "ref": [], + "ext": [], + "user": [], + "plugin": [], + "template": [] +} +``` + +These entities will either be created or updated, as necessary. + +Adding the `+plugin/error` tag will prevent any further processing. Remove the `+plugin/error` tag to retry. +You can also attach any error logs for the user to see by replying to the delta with the `+plugin/log` tag. Logs should +be tagged `internal` to prevent clutter, and should match the visibility of the parent delta (`public` or not) with the +same owner so the user can clear the logs as desired. + +### Delta Scripts +Any Refs with a `plugin/delta` tag will run the attached script when modified. + +You can use this to mark the input Ref as completed by either: +1. Removing the `plugin/delta` tag +2. Adding a `+plugin/delta` Plugin response + +Here are examples that reply in all uppercase: + +#### Remove the `plugin/delta` tag: +Use this approach when a script could be run multiple times to create multiple outputs. +```javascript +const whatPlugin = { + tag: 'plugin/delta/what', + config: { + timeoutMs: 30_000, + language: 'javascript', + // language=JavaScript + script: ` + const ref = JSON.parse(require('fs').readFileSync(0, 'utf-8')); + const louderRef = { + url: 'yousaid:' + ref.url, + sources: [ref.url], + comment: ref.comment.toUpperCase(), + }; + louderRef.tags = ref.tags = ref.tags.filter(t => t !== 'plugin/delta/what' && !t.startsWith('plugin/delta/what/')); + console.log(JSON.stringify({ + ref: [ref, louderRef], + })); + `, + }, +}; +``` + +#### Add the `+plugin/delta` Plugin response: +This is the recommended approach as it does need to modify existing Refs and +is less likely for a bug to cause an infinite loop. +```javascript +const whatPlugin = { + tag: 'plugin/delta/what', + config: { + timeoutMs: 30_000, + language: 'javascript', + // language=JavaScript + script: ` + const ref = JSON.parse(require('fs').readFileSync(0, 'utf-8')); + const louderRef = { + url: 'yousaid:' + ref.url, + sources: [ref.url], + comment: ref.comment.toUpperCase(), + tags: ['+plugin/delta/what'] + }; + console.log(JSON.stringify({ + ref: [louderRef], + })); + `, + }, +}; +``` + +### Cron scripts +Any Refs with a `plugin/script` tag will run the attached script when the `+plugin/cron` tag is also present. +The `+plugin/cron` tag contains plugin data with a default interval of 15 minutes: +```json +{ + "interval": "PT15M" +} +``` + +When the `+plugin/cron` tag is present the script will be run repeatedly at the interval specified. Removing the +`+plugin/cron` tag will disable the script. + +#### Example +Here is a script that outputs the current time: +```javascript +const timePlugin = { + tag: 'plugin/script/time', + config: { + timeoutMs: 30_000, + language: 'javascript', + // language=JavaScript + script: ` + const uuid = require('uuid'); + const ref = JSON.parse(require('fs').readFileSync(0, 'utf-8')); + const timeRef = { + url: 'comment:' + uuid.v4(), + sources: [ref.url], + comment: '' + new Date(), + tags: ['public', 'time'] + }; + console.log(JSON.stringify({ + ref: [timeRef], + })); + `, + }, +}; +``` + +## Remote Origin +The `+plugin/origin` tag marks a Ref as a Remote Origin and associates it with a local alias. These may be either pulled from or pushed to. +```json +{ + "optionalProperties": { + "local": { "type": "string" }, + "remote": { "type": "string" }, + "proxy": { "type": "string" } + } +} +``` + +**Local:** Local alias for the remote origin. +**Remote:** Remote origin to query, or blank for the default. +**Proxy:** Alternate URL to replicate from. + +## Replicating Remote Origin +The `+plugin/origin/pull` tag can be used to replicate remote origins. Since this plugin +extends `+plugin/origin`, we already have the `local` and `remote` +fields set. +```json +{ + "optionalProperties": { + "query": { "type": "string" }, + "batchSize": { "type": "int32" }, + "websocket": { "type": "boolean" }, + "cachePrefetch": { "type": "boolean" }, + "cacheProxy": { "type": "boolean" }, + "cacheProxyPrefetch": { "type": "boolean" }, + "validatePlugins": { "type": "boolean" }, + "stripInvalidPlugins": { "type": "boolean" }, + "validateTemplates": { "type": "boolean" }, + "stripInvalidTemplates": { "type": "boolean" }, + "addTags": { "elements": { "type": "string" } }, + "removeTags": { "elements": { "type": "string" } } + } +} +``` + +**Query:** Restrict results using a query. Can not use qualified tags as replication only works on a single origin at +a time. If you want to combine multiple origins into one, create multiple `+plugin/origin` Refs. +**Batch Size:** The max page size to pull each request. +**Websocket:** Listen to websocket cursor updates to pull. +**Cache Prefetch:** Attempt to pull cached files while pulling Refs. +**Cache Proxy:** Proxy all resources files through this origin's cache, not just cached files. +**Cache Proxy Prefetch:** Attempt to pull all resources files through this origin's cache while pulling Refs. +**Validate Plugins:** Flag to enable, disable plugin validation. +**Strip Invalid Plugins:** If plugin validation is enabled, strip invalid plugins instead of skipping invalid Refs. +**Validate Templates:** Flag to enable, disable template validation. +**Strip Invalid Template:** If template validation is enabled, strip invalid templates instead of skipping invalid Exts. +**Add Tags:** Tags to apply to any Refs replicated from this origin. +**Remove Tags:** Tags to remove from any Refs replicated from this origin. + +## Pushing to a Remote Origin +The `+plugin/origin/push` tag can be used to replicate remote origins. Since this plugin +extends `+plugin/origin`, we already have the `local` and `remote` +fields set. +```json +{ + "optionalProperties": { + "query": { "type": "string" }, + "batchSize": { "type": "int32" }, + "pushOnChange": { "type": "boolean" }, + "cache": { "type": "boolean" } + } +} +``` + +**Query:** Restrict push using a query. Can not use qualified tags as replication only works on a single origin at +a time. If you want to combine multiple origins into one, create multiple `+plugin/origin` Refs. +**Batch Size:** The max page size of entities to push. +**Push On Change:** Push entities immediately after modification. +**Cache:** Also push cached files. + +## Random Number Generator + +The `plugin/rng` tag can be used to generate random numbers. Random numbers are generated whenever editing, creating, or +pushing a Ref replaces an existing Ref of a different origin. When a new random number is generated it is represented in +hex +in the tag `+plugin/rng/6d7eb8ebb38a47d29c6a6cbc9156a1a3`, for example. When replicated, random numbers will not be +overwritten so that spectators may verify the results. Editing of a Ref that is already the latest +version across all origins will preserve the existing random number or lack thereof. This ensures random numbers can't +be farmed, as you cannot generate a new number without cooperation from another origin. + +When delegating rng to a trusted server, users push their updates to that server and replicate the results. +When playing on mutually replicating servers, each server is trusted to generate their own rng. + +## Release Notes +* [v1.2](./docs/release-notes/jasper-1.2.md) +* [v1.1](./docs/release-notes/jasper-1.1.md) +* [v1.0](./docs/release-notes/jasper-1.0.md) + +## Developing +Run a dev server with `docker compose up`. +Run a supporting dev database and cache with `docker compose up db redis -d`. + +### Build + +Run `docker build -t jasper .` to build the project. + +### Running unit tests + +Run `docker build --target=test -t jasper-tests .` to build the tests. +Run `docker run -it jasper-tests` to execute the unit tests. + +### Running end-to-end tests + +See [Jasper-UI Playwright Tests](https://github.com/cjmalloy/jasper-ui/actions/workflows/playwright.yml). diff --git a/jdk_25_maven/cs/rest/jasper/_config.yml b/jdk_25_maven/cs/rest/jasper/_config.yml new file mode 100644 index 000000000..41b4b1de4 --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/_config.yml @@ -0,0 +1,3 @@ +remote_theme: pages-themes/minimal@v0.2.0 +plugins: + - jekyll-remote-theme diff --git a/jdk_25_maven/cs/rest/jasper/_includes/head-custom-google-analytics.html b/jdk_25_maven/cs/rest/jasper/_includes/head-custom-google-analytics.html new file mode 100644 index 000000000..8b72079ed --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/_includes/head-custom-google-analytics.html @@ -0,0 +1,9 @@ + + + diff --git a/jdk_25_maven/cs/rest/jasper/docker-compose.yaml b/jdk_25_maven/cs/rest/jasper/docker-compose.yaml new file mode 100644 index 000000000..b26f6ab0b --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/docker-compose.yaml @@ -0,0 +1,29 @@ +services: + web: + build: . + ports: + - 8081:8081 + environment: + SPRING_PROFILES_ACTIVE: dev,jwt,storage,scripts,proxy + SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/jasper + SPRING_DATASOURCE_USERNAME: jasper + SPRING_DATASOURCE_PASSWORD: jasper + depends_on: + - db + db: + image: postgres:18 + ports: + - 5432:5432 + environment: + POSTGRES_USER: jasper + POSTGRES_PASSWORD: jasper + POSTGRES_DB: jasper + redis: + image: redis + ports: + - 6379:6379 + redis-monitor: + image: redis + entrypoint: sh -c 'sleep 10 && redis-cli -h redis monitor' + depends_on: + - redis diff --git a/jdk_25_maven/cs/rest/jasper/docker/entrypoint.sh b/jdk_25_maven/cs/rest/jasper/docker/entrypoint.sh new file mode 100644 index 000000000..26bb2ca28 --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/docker/entrypoint.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +case "${JASPER_GC}" in + zgc) + exec java \ + -Djava.security.egd=file:/dev/./urandom \ + ${JASPER_CORES:+-XX:ActiveProcessorCount=${JASPER_CORES}} \ + "-Xms${JASPER_HEAP:-512m}" \ + "-Xmx${JASPER_HEAP:-512m}" \ + -XX:+UseCompactObjectHeaders \ + -XX:+UseZGC \ + -XX:+UseStringDeduplication \ + org.springframework.boot.loader.launch.JarLauncher + ;; + + parallel) + exec java \ + -Djava.security.egd=file:/dev/./urandom \ + ${JASPER_CORES:+-XX:ActiveProcessorCount=${JASPER_CORES}} \ + "-Xms${JASPER_HEAP:-512m}" \ + "-Xmx${JASPER_HEAP:-512m}" \ + -XX:+UseCompactObjectHeaders \ + -XX:+UseParallelGC \ + org.springframework.boot.loader.launch.JarLauncher + ;; + + g1gc | *) + exec java \ + -Djava.security.egd=file:/dev/./urandom \ + ${JASPER_CORES:+-XX:ActiveProcessorCount=${JASPER_CORES}} \ + "-Xms${JASPER_HEAP:-512m}" \ + "-Xmx${JASPER_HEAP:-512m}" \ + -XX:+UseCompactObjectHeaders \ + -XX:+UseG1GC \ + -XX:+UseStringDeduplication \ + org.springframework.boot.loader.launch.JarLauncher + ;; +esac diff --git a/jdk_25_maven/cs/rest/jasper/docs/cursor.png b/jdk_25_maven/cs/rest/jasper/docs/cursor.png new file mode 100644 index 0000000000000000000000000000000000000000..90efba9bedd663d1887ec67f2fb046891bb3f6ec GIT binary patch literal 39351 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 literal 0 HcmV?d00001 diff --git a/jdk_25_maven/cs/rest/jasper/docs/entities.png b/jdk_25_maven/cs/rest/jasper/docs/entities.png new file mode 100644 index 0000000000000000000000000000000000000000..74bd9334982ced376d37638218b2c09956397cf5 GIT binary patch literal 138663 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

fpoVJ zQ8#@7#po(mNwLcDT6bL-*|&jPA$;fBSwZH49CD0g+nb-KNy(WHu!M{7{aHXzrIt6- zFLO`8$AL))>k`xD24#eqCf2J2S!Cp{i^b4F1v|VTizK57@1GXAU1DFR=R7m};(CE8 zCiE)YG`R7vA2&TA;N*kpUsB3Jb{D|>9VZuTi2QSqR1&&DOFQDPEbwK~cV(F6F7Dh`J%%+5W3=V<;} zhiZZq%Xe?7+23a=P_ti)=0-eELWNh#)U&6GL>Dt1oA`Ne7f~!oEC{Q+shSR^A6*74RNs2kmqYw>mMTU{T|vIRu5Lf`E>ha* z=Fgqy(6|;D%mPfe+6RH;!I~k=-H7d9U8sjVyyUCjbV`g`<|@`5o12?U_A6z_!UnP} z5S$LS#1^EG5)_e9~kBHI`5FO~rB$+=g42nBq7br!Y(UZA)m_lpUQ8=shZ#{WJ zzC7+uB#fImR-0V=kYc)pKrz)@avyjg8x~a=ux1%8^m=<(B+NfR*Cxm_sVKh$J_`Y_ z97a04FUfbJ;Sph-?Ax&waasfYHiP1Wzhod@(0lG+Y^dnT;$_i^sn#8E zm~vo{Op_)Idc=941i%*#1JQeC5Qw%TzPcO_VCWz48~4lGlfYwntJPXVAFyDuzb=gN zjjaOe7qyv6|FA3kC&!LpnX!!cKhYYEOxCtc^Rf$VOD?!p&kR!4y3+Q(6KIJyvzoov zdQ?*_Vp>m}2u=xTQBul*zd`8Bs@_}V=xE?5ZDi!5!RTvrVj$L4SgWt^v!R^893Z9a zl^ZU3Aszl-3|RRDZKTo*{Yz$QJN>n4E7HnW^rs=1Rcs%@6I&zyK{+Ryn?w=<+j;9v zw`S3DK{YLq8|qEMRuNov+B=}kpeefY`IcEQgA!3u!0}1cdXS$H(Ta`N%C6Ru54Y&> z6fAS=Ja6DwLueOJRtlm2;R!EMRD1q|-E!aCLNuiA6NHEh3kyP^8miQM!9uXAp?0g4 z(%~ufK3|sP*@^VJn3QN68_g((gUH2!F|pN`sZI0}cztYA0qS%azO!n{*Ky!E2efBU zCIT4}xuj-?Dd~sJrXJ#7QW>aC}44Vi*iodZ`5@Sq?C|P$-F9DjxXRU6$S8z z)C|DBj}|0&Cq*?W!9o*X*}Z;wcqdEeH9 zg3K1k*!a2e&(PiZKb`IA;O-rpHV$(!@BfddO=0^xyKAsXqI&*dqJ3T>V-iEiE7*ihvCv=|#;>RuLNX#qUf6 zv!vH}Q#bhg4<{F}bvwWDckF*OemS3IU%e8z4!MRGudrx^+zEMA;&YhVOkU?z8I~>m<_-T` z;>a~T3^rNqq&y)e4o@!-=$qo%yH^3LGK%zUF>&!}@Vd z*39bc_|!7uE8ZLe!h}&XF}S&Rv-V8~rVhIK(irCQrUuT`cj#49Ypv>!5t7}oDiJ(r z1I!?})1d!EkgL03Ph!kZ{F|{2L}@g!3M>M61%yft5hdVxUy*@_!^3!kQMlpDv4hh4 zFS#Eo?{$Xn#zw&(ot%6IADz?Dw!$sLW`+-G?e4)8RMDHJ(5i1QE$#dFZR_?r3mn0* zj%duBCXR<+9nqfejV63|hlAk7V-Pgx?*5thsEbhSFl?U=mM=&8b`r;aq;vUefxu`D zDhRI^C@);R@Z8L_zgmT^tHK?;s9SP{vpg5FzS6089U2dB97k;SVEEc1Vc5vT@Z9rG zz@)72)*B+lmy<5$|AmpQ0j@EUW7*<`KLZv%gf_k8e&@NLA}BoVZ_I{5y94^}p^dco zHPE4iSkgLHV!@mA@#9tU>*mUTrO(l~)ouQa69_16WX7j2XABOfXMR*>Ye0vB`hLti zPaujn2B?H26V*i@p`IOOC+V-Fxt^js0xlLa^cBdJ@R+u5qZs}GfJP{RP!M#z4Kol! zNWEGiT=NrNi1tF=IIfL3Od9&sD#vA6Uh7mI6E865Cwv<)9tW0Wp%cXIQ*Q*9QqlO_ zo|fYSp>mAhEV~J>-%$0J5=#`iPHx9XFib<%p-s4>aMykl77{nmJCT!Ol=1$d&@3cG zfejExjwZm#THCsV5b2Objf{7Q>XD^0*Ym;06T=R=+0GpCpGfWT>s;Ja&~ee?KkeQ=*pg|1%!JvonQ|poyat`#&Q{{bi|TtERJHr1v(FWS2Ew zI7G|`Phu_x3%j^$BYd>ZFScC$OpQg4nI+UAf?XP>l||DO-RGwD{;iY%jS|z_6WM01 zJt`_LNKpNcG!qfxvEGh0d3+^t`AFKZ{%O=1BE(C7S@=S1S)tQxJm@OIiZ!oaYkSkJ zB@~7Kt9%>!{>Mhg8|x{?8&19ghMI6`?wj$90wk^KXs$!Io0u7BHp*{1w3_ymO>5?C zT#+)N{;%KiPt~uKdW*)ynjNp%rdtb75}nlKpjnCPCRb`675HK48L=t9>OIrAC7T`Q zRNZHvO)`KZMnujgpn}aixqQLSYSOTt_fBeIaIM`u}tiRVePc5SJp6@SbRth5{Zz|&Lq*| zkscQB9t>h~=!$~s0w7^yppNfDj>{MEmeTlax!D;62rm$sK$Iafyu;@K?}ujwqyS^l z9jFfdK3o=ZbRy1aLENJGS3#fY(z>tqJ)pgo-aiyRe1v0DhAVvf+fNadz<_0T=OOyBePI|46Oel>3`nX~vZ! z(_=PPok5R_p{0QorZ;y5se;hXi~pS+L14qvPcdk>&Qp8SdUUlvvV8;fkpAbrl~NP> zfT5?2u2>Nm7l}xo+LiCde>3_iFE}Ut>ZL(!xaZQ?p}>YLFH!{x5E3DMVOFxd^^G_p zgv22*{-}?)R^3l=@+gWNar2`8icV?DA$5TQg9DM|D_E(xYu6qU8A>mF|58m_1uY95 zaRf|bhhb1CRI7DanGQ+@g8!aN8r>7wgnW3Wzv_*~Y6txO5@^Iy$f1A$9N?1}D9=n_ zgwpH4Ldw;s&=QQqzf>XFkF|U9O3-x5X@C8HKUl!|8R1$(NG^fg9 ztI2$%ku}2j-%o$E`T>ki*ziF)LeRnX75+bEB8aBkm}ayRvZFq2e;h^l=f@eLQ&)wR zg;&>Ce-n6o8JHWE$1pZeP zZ9{3~UbdA?78BC@c8!j#@Nx*YEvcN8dQK7bJTe8K+xUqerrPnr=k@=6;%ooEhYLQo zchZ=}u7pT&IyrL~+9D$_KUsF|9_T??cSznuJpG2X7-ml^`3xN6S2zBC zB*pZ$9pR_u+lIG)&vQGxjh2WH(KE4++pawOk$Bl}VSMWgEcNM+^iup0gzc zicpP8h4ez~;#O6Y@86q`sv_1?l09v7&vrWjmneI0Gy7u&68ScOiA_ipQ2bs48=L%$ zjuDW@?7!V!+jm%0iGaT=q2BKCyj$uw&?7PPH*6h#JCrpiMHe`Gablv2-T3#GGh3;} zuCSG&x-XO0|I;0ZO#{`t73Y<`YfL8lxp*g>qIKu#?A&KgOo<>DTsKp!HnYSnB$#RG zyguoysyIm}b!4bBS~N@-k;JvcKA+(uJHm}2R+)xJxwNKXHX@T5E_sq7NRg2AAOsP< z1cd%}+A)VO=od(y^Ef;21rw?Zh?fSMIt~Ync4k7j;fKvjB3_2)UDoW35NEgVz5&`C zCth;`?rdqkcS_zd;yN2LJTTbnt-s#%PVBbM^)< zqsOx`W)VUw$KEN%ecrE;TQVa%vBdMdN_SaBkx7PT2*i=-XpnYCD)7tJ0g8rC40M#pjV06ENC2ptB)!{5eVO)=0WoV{e zp%EKIL@ssz8gZ4X&*DS?F~R&HB&v4>%cM){c5>irA~#Cd8vvtv2&vsAPB;TCUF9dX zcY(=Yg;clO(OfJ~N^aXLHX>0*M2Psur#0KShu-QUM4=5L=tT z?64Px2~C;rpyIzBr}I2yKvz2PWz(U16ZTRO@9W-!8fVB4F|bA4ceWx>eW3izlyrt7 z0)K-XCQ0z^#XHla@(8X52;rX8Lpc5aCYGe)boBJ1jNk^so4P+ud?1A_#j;qPBl13i z|0oHhUq6{O47rxsN9$(@b?zTzJj{`}A%{9H#15W%`4M3^Lgro3yt)z{tOmXL>i-dw zS&3NNj&o4c&;X7Nm7f7Fvy*JCRJzWPpUGVQN1E@RtgLpd>S#OtCrV!VSLIlYXEA#r60EG-+`ZHPZHkuIyJN`QtL+prx!^#Q+rqmfw#tQgkV zNcZHkCr=D5&izXX{qiCSMp!groczZI%30n&k|Q!*`~D@l(ojSm;@&we?D_7bV_m8XK_R>A8wdTlk=G9h$IQf**dTc`Q&2(uZt zK*Cc;NNh=AiD%0{>A_WItWm2V-2)oMHBR-;46OmizuM7bAB{PxTxfSM9igx?&g*2% z1Lg1OnyqOu8XXd|yc#nrd~z^A15pG(0WCVSysp6~ z=imWHJYWewLR-%-FPW5$pvx->{3w72g8vkb0RWK5v_6wT3R?Q5$OOgg2rq7rq(^#> zKEj4pHX=36932lL&h# z*=h*-p*)ch0!s}}ex!G+b$YX}pEnZmWf}uJu3AsoN*jg{QaEt~qS$rVpzB8vyBw<*%fH`>O{VpM zT>?-7u&@$?l-cB>B$>TskZ*aXW_xFH1ir6>t?6!0KYK!ie7QzrFV+&#wHt_WP zdO`{5boWkvB{V@xot^r~66?lz(Q7Cx#5p9&GX$(`GTXgtj}A;;)yUNe3Y?7Uh<*{C z#nN2dk|#Y8g&d;S?u}!q5qpT?AQkXm`Az0_Kpv4~%0rjO@Qul?M%`^HimQD^Lmbrgvp)6O$g`|b4oe2loXMDXb& z%y$oL%=%Aut00-3Q6gYOkEgkXka|KpA9$aYLig3hG$4m+HY?61HIzUnUF*C;MR7rq7Z+`1<(tzxCQb{|4ai7ZFS_k)WL+WLjXm5* zQs~2^uor(0E2dLMo;m*)SJxIMvUy5M<#f6xhZ7KC<5{*Qy}1^BzlKi%d_+rXUfE8V zA_l}sGr)t$5wRbJoZD!U9!l4F(n5fZgK>9&{A(m#6O@v17Z75HyYznZWmE2*4-sup z?bJB{*}}>CW&}MGDhX0baB+ZERwQDc#L1r-fg1_LkG7YHT|k#S!=RH8 zdw_7Eur-e{f`_T&Ssr$Z?|X6rItr{#FM1Akc6SSftjmP@wtu7tNDYf>e||&uK#6Ci z{}1Em*_J|&3pu3%XM+IZYtBl)+vm~_qTWsST(&btM2+SW1nj9X^j5_NAq;qsCB=?)I#Uf;?z)E zaqD1_>+oMAPq^mjgWsbd5s@HS4pWVSo7USVI4$(4!#3!SiYwmn4{336kpPPe-J*i6 z*2d=`6%Jefb8v^$W+`(L_KXlH0;uF@?G*OS(UYzo^>ZqxoOy`X&TXBcFc9dBRn3U8JQuK z9cjoz8$Q2ILwaF??PIJC0iNlQhzxQEab=hhk9_VGsGT)EGFE{cCy`KzC}FE0tZ~c9 zkug1agI}ilvjv*Oo|u=VJWeNg6|R~8NV%J{cObc)Nn_!Xw^zwPM9e~g_0|N<=?}`v ziJ^m3|4BB6zXKEIh~VzJmXNlnbrnXOP&^9$=yIgo}dQ@Db5} z`nK2Ofd*X{c0O8~b6OH~7?Z?P&SowrYWX@vV-;#oZ6FttbY@N))Q==VXF-xp&%DD^ z(G(ZoY!P;0i`jQK>6^QOlz*p#ajDo^UY0!@T$)crq%ypZ=l`>* zXE&YB;&~_dt;wlsS~R~m-$YwlG~`j#QZ9B}a8u^mtnbh!Nh*?0U{(HoS&-iS>`H!` z#+{DuYFWlf!-Q=M(;m_cq?^H9=DgL{-zgDWEE#j4M>>>tPXvV)j(cS*{JJPU z&UwfqR`Z(}`<61XX#G2PUsTI)Tl*k-=#WOEtIj`i^&jSL3QSG8kNDqtz3Nf(nsanD zRlAG*%{u+3^rD+XeE((v9N*N~WH!YI-VM(?dM9pTro`pwRETUTNAI@m!{=2d^Isf_ zr)RF6Y*f=O(l=tam}s@jYPkOVRl9+4R)DiUUr9`@twJR2U3HeXbAD+-zrvVh> z$vL6Z+r_s%F6QZd4~g{=m8-(#AKr>y8@u&cU%R!%8q)(0)%`!;+pGTjY2ai0Xur7G zYW`S{W8a6#s%#-Mk*6m*n$A}p6MM(DqpS06%ff*@Qg40)bZm4o+|&8sVO@v4#F~gt zY3uW({2yFaeeihrRP+m0ztBsQ-B%k@-)@UHxc>iIcw;I@NvXgQ+&U@lFVKyJh=PEmfPjP`jfj*epkM$hQqtYsjUti)igbq{Ewzzu z5J8ZRO{a88*PRQ`cfRj;pZm|<&w2Dn?7iNabIdWuTnpzr`f&?Og0T_FXi!dfALf8; zFmn_OwS0{a-KAS~xJ-evnVYx=!_1IvdghjQVU9fVmqFpPbpM1Gr1zIun?|>mNr=9` zNIxx2>54zwre6IahUf-c=H>9SoWCp8@vkIu_xEhNl?_!T@u>T`r_r19_@^5xo4%eq z%s%JYpzm4w)!|;Cn5tsH`?X8~)wU4~Bd*+%l|Db=?OeWnwd*g#;bFpdCS@x;qWcp^ zz2J8eM_xMW5qGF2)JF5JdhEu?h_lOHr>JluW<cYHj}b z?7(kyRww_1o+&;#exv8wZzrL*=vbWSzRBq?#FEZT$?YGwP7j&6A!lRMK74tXV& zdm1utbzHkAB>ly|1=pXZk@poGjcX@EnT?d|ZV7ZPD4Q5MB5G2C-%m+%r_h^wa`RkOj}vZ3_9gDx<=&R&pPp)?wXaj~yzXwE zK}`;&it4_^h`$S_VLO<(XjHXF;gNS}tj-krHmrL1b6>H~BmeBHeb+4uHjm%>6%PeT zC-!KdG@e?Vx>vE4(O{PgH`hAK61$Vlat0MZHA&|dRD@oPE3|LWrJ`B^0g%F0&(ya} zl*QH~D4c2QGlPE+W(55fm*oiE=U|!iFd26DrTCrL8|?hXmRPTa9l7ZxU8F9wD@mye zqs=gey)#NNLH&;y+syZ7iH#d(C&TsICwI?woVL1Hr0!~JO`QH%wVR1}xq`vSk{&h2 zBlnst=*K(-Yk(SF$)nC!J5Nb64zV;e|5(A?B=l%h8_H>>vHjTh%l^+K9&uUOe3&R<(yBiXN%Qf- zCAj~`6~AnZo!ws4_v1g-lM8T5wQP0MGovN z5As^N((=>Mr<{q8k-yHwCnNN*OSe5dOnI>dCOVJPsl*|#9_Hmb&5bLWy{7q!cHI?K zr!`FQ1*}r8L##Z1{z60T?`ab4>jAu|vRI~0HHc1){07dP3g+rWaRn6bNnX=xDw0)D z;wH=A`^;WjS*T+&tP_K451V{TpXwAUOSyv3O1~s;{SGrZ*8N1E(O{JRuCO8zY1c~A zYGg(=5^l|-FCGk{Rp8ebNlC^dOK_2id|!HPti8SMuj(z|fx zSpDBiXh@O~hI}L-q`bjC8sF{Jurs+dOH6>gF0nt2!PAuMDY6?T^#4vl%UsC8R?;Q! z)?GQESun<0g{?FoK?VWBiJ7`G#N}Mp^tDUWsi6j?4^_)X8{Ud1{p#!r+lq>HT-zN$ z1vI~lnY`xzs0q@F*fOg(?7Ht+CH?WX=ReY(I zFI$wrxsJ$^qvmXT%@Qg=Lnn06gKM|YXqYiZo>;4=RBB=qUOV z&QQo!&YiS*W8=ws-N~>A(QMAI)PQNOeYLzm-KM|_$&%SuJ`aW-|y1luoI+}gt8 zt3z9yklT6V39Wi@7YHp=lq2{MLWQVrQ9bv+4)f{b{4fv#-sxkQeb~M<^mi^3jB84c zeDJXEItu!x+I7ZRT{hi+jL%Ukt!3Z|Mh6?)+b$&Pf<2!O^S0m0bw6y?JVy41F>ZPg zl6}tO^Crb9gaTZ+|L7zIhMeO{d3#)nt z7L9z8n*u&1v9*Y{vxyWa6!kBqB$Dh~P}Y{K6il36y*K zN2@?r|6Qv`@+7o|1f$^e)~D#K-Sw&^W-hbyX$cCJ4F2jUET4_hK;xeh8TEA#`rWq` z1!I38E6W4PxD|bq^(MV@y6b0BmUAf+?NNaktBDS4knk@4l;7+7#o-cFl*(Z0BSMbAheOSYPdTDXXe5o3F(Ufm`4bxCQ6XX#^%<}mzDN^ zfsp9qt0puRXzbogdxNI{cCA4es+*VB-&Y_=L}-X(|HXQc^=-Ki7feIcl}Y(hv^tD9 z$2O_T5?_$g8d;_&<@XfwI=IUTV+LuFk%%Zw{MJ~+mHA}89ZWV}7Y}#Yr!Oaa z&hv{t?4sxp|KWi>c*8lbNxVxIvTg0@_qH z%nj1tz2uH9b84qh36Q>IoNi8q6 z|LM6Io|IKi_@mWih{uY4nVg?6+=v6hHa@D}(P-b3laC!)T!KrTqhyq)=qMx)KU~Sk z%BjgCVj347N$w(vRxVyTqmea5AZ>@UU{7D|&XS6~qb!_06xy_6T5~^0Q^rW(&mKv% zv;z-y0QTg4aq)`J2U^5aZR>7NXtoyc4Smm)jUL?Wt=uD>nwp_)nyLh3TF)M|9DaB? zLvk|gxTH&WDc#h{JVda06~cpB;44}*3u%96V0p0(81+xCZeVUj7!T_bQ}(&jMn)XT ziQ~?wsy1t$?wnF;*1gP+Iq-!%-BS_@g|D;E^XOH6MP zeK|DXL|@z7i1q2~Z9taE+-?8r8vOuA)$ivvGf!ZTAu)Kr>HX67nP(Dj^q)3~2u3!2 zfTT`_&nWHd;}l)`l?+3~hqrXCJGQWC+4zBeWnsFX&5dHI+dZ-9iHhdl|6tmx<+01Z zELM9lE25E+mtdF>uRfGt$5Q%H0cr6xL<%IvL{y!Dii0xCjZ<;f#K!8QW_52FI&mtS&fR2Wj`lm*4sD zx6|n=5FWTNokBsXg(GC~Ywwqa)P(`MKj};;6vcb_n9=XlLch0AfM)@4YY3H+;nUI0 zS8jZses)8J-`nOIVTPpFoP+>(H--^&I?*&A&NSskWG#~J2V^@Le&3%e1kgxj?reQr zzP3klB|HX+2_)Ndn{1Np4)~jcnNVeTh>rVV4-awLZ}E`$zK49AJpUrQXCDxk#bW&< zhoJ4tn3qu_B$R*1y3eX7%)elh&s8z9vkV9Mb@zld#wl zz{)?3;>4=C-<8dt+gCic>RmEnm)qT^-KZTj0b$X#)5_!qlNV|X9-F;p&J=m5@>9g9 zy8;zJN)XW)iY$E47IK1wWv#X-kxC3Z3Ewq2-E&CEtZTN6 zZH3G<>CW$4u$vM)3@jnPN7C}JhJAQUl{J!+p4pVK^HYoWqv-ersPy2!(9-jecom7t zUaF``DxTYsDUx)#b<2fAzVd>vY!_yIUF7w{TzQ|AgrDy4{)C-xnn6cA z$T$jh6J_DugNs5ItLJYw=FYF7bGp^NhLqawGa#!?L4%Av&B9(x(t-xc4thq0x{ft7 zC|XRreD~{@1flr3pJ>rg+H-S!a@rTbZZJSt_(2Fm;|;U=7@PcxWRh2~Y*TGkS-$=? zb@Zb zTDtlN@6N!Zx8IurL{Y4W%s2LSNJ36Oms0u*(s56GnFzU9yS|&aGrpl;Jh%oFU&&*k zc2+gsMf?Wz@YQVdF`#48@E%BpP20s4Wk03;S}#VvzUID%10%GA1GA>s&lw#z>8Lat9q3bII_&*qhS?L>1qcGkX!hyq2JL$j$r# z;B4z6&pRKVJ!#l=s^%5%zchSMou5ePE4FAGxC?)2K%RkuZTrWAG?UDyNF{&kYs8(; zn#eG|NK=XJHn6y)vUogJ)#+`NUuKoqu`c+^X%)AlT?{j7>1wQ?kG&(~1;og@KdH0s zHL-Z7+Qre-4J@>94YEBxzHwO_eP67MgWELN3)lh`;HySEceR+6!uL@#U@;Gb91zgW zcUY`&oluU9eD7-5YVp3 z?h8WXgl`RxWbe?3pMh`;fk@_H`B4Sjb*JWPnQX_GeFEI;vjYdpl5puqY4OpjUmgiF z*_?iHL&bTTuNz8QAfHx)Yhyj1oDBUQGIX*3(f|Mfz2x`Vpu9rrxKCkn_uvvOwVV9l zPBN-x=c*MMbl*b|L}}bkGmzPC|CE|jS=UsIW<{Mj<2CrlAqZPaV!rYsl*d}cW^kP1 zF5+-MN!kh<$zBi{_E$@B$m~+gt=#=Bw~GE#0CY)D_*z`fgNd% z=_Fd4_+Z<{LfNq2`LO}rC40b_$3Wk~84jp5E^>pgyvdmc;=JNqzZj(Qw%95Us7@`y zMm@Jfoy-2Xx@sS=)SF9I!%0NOy7rs4qVjA5gFYOw00=JS=-W9>m?sd!=WDeVpFGiX zIl)=yE)%8Q5{lm;#JRF;_UzkLnLp;kXd*b*FSd2Z;8G#%B$Sj)hWxIG{QWhIl9;f# z0-hrSpZ`#j9= z@*k#bd{1S(y>DjfrbahkvO3T{W*&*1$HYsuj9*G;(3tEwAzTyZV!uSeF0V$Iw(79g ze0fJI&I3}O>PT-uwFE&GGl9tQ4iBVFEhi5V0C68Zht2|yOIC9xhG1n;-bBFhGuq$# zs94bNN!yl#^98S)5TK^LB#_-AemcNcmJXGh{a#GTkpc4RK}7-?kC>iDdDHdpSQeiv zN9F~fv{iKq_~;(9(79gw=<^o!=qD&bCPq~az4=Ay2r?~{FP-b|R%`q}q*B*pp6#~{ zY1F;lh72s@(cg=#q{s54SL&cV@aO7WP!0}>#;)#vm2(po<~$A_LQBf6MI+JBZs6me zxDVI=a%wcx-PQ!waS!Ta^b?JZ9xf@UPsY!3+;iTHQC+cy-T1gIPJ7j7&*%81`oXNo z@%>4B0jPN`?v?I&y4IUp6u-Q1EV4I|Q?}gbUVOxNWSLVKjrmyZ?#BVRbZBoJdy2As z5@p|EzGCC?r?~1tb)g6lK-JsDX5;(H#ibR(eb|k(uq%<$iYWDD!nW201H|r$MW*IZ z`(o@xlRE}g?laTVdL0+{`E7SDp+kFFgtCiV7U$)@kEl^kOybHVok|St0vygMTzw6{ zRG4POeq)9$IQDwdcZD*?=9t=-`979r2 zT(=Emr~ANZDv-2_{g#y(yDCK$7<-3Ve(+Sa@j~f{QOm8}xKP(KPe>|t@}5G`m%}$? zV1HGbTPePHS_{dko_h<93@Q;~C*y!WZYcsUmyq~$eED2SNM4kY+<_kOE%M>c+V(84 z6;RdP%+TT(!sy_?Jxh7qZ?X%C@|oKj9YR24IFxNWXy+FIG-_BEb!Jn3Z%KX$icypW z-O8tdO49I{2ap=qMGh%}#eMj`R6%76a)~14EW|4Zz2#GR#ECG2K+yo3Go+=&6nSwS zLu>$roihU-gdEZ!?SxjmXa>d-AR8ASNl5?=qDXSpJnYKjkq^JJ>kD4adjPkKLP7D8 ziIV__Q@mBZbJ=~^bgcowrPwF60rJQ5_B_aS<$P+RZ)Qowk-(%O88P5;7dava6A$t- zZb5~nxW*7F;3KC9DeFV{F^*Oe=_C+z5t5MV8{lqMXRw9e38&8i;rgq)I{*@L!!%Ih zcKnW|V-2Li5P{&EcL0h^gguyqIG?AR`fQh1l&%KwMIP&IwJb7!Ws*x0O=W96eWMV4 z5fl#L4~x@0wnWE}m85*k<{?CjeB{hmDyDAtcUz9RnCJ2Ab495^_CIosO3 zSWC;2-1fjKDD~i0LUkvwooS5*0ZPa#Y24pemSP=>&I8jGGndtI9N1@x2(kA%{nY;0FgVKM5D+~+@Py4xF#%O04)VqQUVH$R6KxAd{PUuS z`%nG{p~J6#x&tJRfOqd!8u-P%_Ax{NL*4?V48ZNVoGLDST1@P;-lOm6771WpqDq|( z7Q)pb%TU4AeRb&^LRn#ZtKUQ;)$LGkFANQc{2=4Ls|kRjuT(u+x{C(N7?CRY%tvZN z_cASP%9UMQ{7I|C-+WSiWWY<7(Bp;+Jh6((vM0z!uD9Ehf`_PnPyMp6hER~%ZjFGQ z6L|m6(X7LVyj32zO8BTaBN|o)N-oiG6dQp^6!;gFX$#ajZ@)#rULgH*G z!T(I%-H1C3Nmw;g&yJsyl1xIahZOI&Jpc1nLY$V5#ZSL$POMD(TlYO8Do5VN>3>&X z@8oS7rk;(yAa%j=s_jWFu{nw9|FiLs+(TjU6mRpryeAem9`$`-xlVAO-+#~VCDo@f zgdbEjtiEjbbgLm%X8LeM)!$P13Brd_cWuz3wp z4x&mPmz!L1=GBu4-~^{CIf}DU_ZIete=prfYr^Jy$YZL@1k~>!5kj(?@%WX1poN&a zeap|4)CKB_Z{gY)eM~@BYlPa(=M|+dpvHg324Lg{zRq>1|2+RYytgpC`Udu(cN6y& z=uI%bpf|)Rrrp;CT)gPccL@;>N;ys=?+jJ$q*L7`Jn5@IH20U9G9i!*;T=ADad5%< zZ;A>5fJ788lt#seP`^JS7g{ zO>qsoiJ48;Ewa9=W(iZ*W}yrZJ1y_%C`O0- zV0mQplHv<*SnzjRLV!m#7RgAVX1tW%o%m!S4ybg6AA*kY@dv9V<2$YZ&+kKFfxOMm z>j&bh>(C(c24cdm&d%DJ->Lh{SG}HZAK}>#b!^PMkdpejvsHH7a)0Ude$yefv~+%8 zm9>MTjWOOPqO@?i<5aQ=HEal+Y|IKD;%ElLRexoHR?Z7ZDHu z%D@VJ{QbO9K(e#B3=|q7#|J!>r3buGQEs)K_;*oW`<|4a%zhl+7~Lr|Z#8f@7$+FA zl<2cI;xj+;XgQ8Q$5!=wd{!{8tNxl&AEdOQp!IQ!@GDi<1kd4@j+omgMf}U zQEb5@J-MtCqzKDC{{arGvHBtIErr7GFP6P(hfRMimH~dNT->@ZPE}4UN%+t;j3e*n|~ z34fi@h&FXsCT~yR|E*06{VnkC*;|}P&dwJmM6N}x9??9BBJ8OC@#F3rNJcbIN=S{h z!nq9xaRJDK2&U;g@ep>McTW1ijBR-15ouIZl;!#kEtIM?DTW-ukEzD~;`08um`<}+ zDD6K0;)4N#j_Y~)1S#M0fPe-}_KZ^d!EmZx%Jtcmsz4?DUb$FXO66ka(|sS#oLGi@NC*H| zp1bAwc|JrI>=qP)$OR{vWdijLzuDFVPf#iwu{aI81`@R;=iW0zB`+!8IqO*t6iUzg z=S0FLL1JfjP2xs<;R^jVpwp}GUq=o)!lxit_&n%NUiNhY+xyKTZ8h!obdBEvW;Ff~ z&_3t03joH!EtRDjh=R3K5qDqi_w>Qlm>PL53XhzY3pe67_`8YTLdZXP&GSJxCfMsH zqV&0IQ&~uP!OJ);+a5{@sInq%)kJ&tdLKC+oq`xtY&ZZK(Ub>r$O$gd{GB71CH3E% zYzSRf9FC2hj2{{!>`fjdJUD0dYN;57Qs~>~H*l2%xIC6HM|mFk$fWxkXS_X-T?1S~ z9bc3H>UY=e3J9F#n_Fe7uk@D}^oh8TW)}^!GEZv>RnMB=aLj-u1m>@qJ-Yxj zis->XymRnNP!NNb(8h=Q`_>+R$CX6=ZNwtEW#MA?P$2wJVPRqabmagY=$)uwYX|q@ zEm@yXPtUsGM?Ws!1|jA$A!UDBP)BFyDX654+u4rO#TYF!(lfC&l!*;3o0TAhu5=w5 z8thcItbqh^M#G@|p{Z%~3CAJ6_4+@b3G{7B%fhECQ~2L2D`7CPjGRDRmFH_vzwxBL z`l7i35t{wqW20_d$@%`n!CPho>lu)=K;(pe~Ve7;~DO#G(oMu2V}?h=AQ+a5c{RW>cxfIYT`P7M2ONd*VcG0WmV9)_k1WPp;Z#Uj85I{QK9@$(}6#Otu zCc<+e0ZHvhA6VT_GGj+BE0v08OA3bAZ$?VF^F$KH@thQkZBUav)6#M`D(O)N01RIBam^Q>( z;EjVIg#evto~NcxH0bFJvFv~5j8=LFE4?J?L73G_LTQHDXZHLSqW9urOTrW4Bg_z} zNo6j!;owe}o0~#KJl5|U0;LxqquyDjL2}Z4pC_=2z)o3AtyJ~R=&OvE_Y+SK^ zm)8E{c<9-UudvmCs8ytwhDfBNdkrD;5nB=zLY;4M&f~h~54(!kADyb)+q-(j&GHsk}iHlA9Y)zgIf24))3*tk0U z1+wmSb@wELBnH6d6bhKNTF@(Rvl+ATvw_@xMH>eNj)B2pwBaJw*ke~=p#8s)9k+$W z-qJkv#xoVTln+?$`1`ZFxaZ@zhS}9a+&ONLaNKeQl^+Z)Z`xm;MJ~Oy6-~Bs;wq4Z zx)U&;<*H}$g#PwDL*>K44@U;kL#pPB(hndNMBK2oEWs}uak>gTHgKDWU1@93)wRit z>%^S~S=w@Ji~u5ux9GTlM$>ad^(^{F?AouXpS!%h7dVXa0LIR-HrA*0H|*%Irc6j0 zg`$1gSK|~UMTAiW;c)^?Qo+p+Uu6qvs=Sj|fi%6kwk}>fbAmBDN18QkDK?`%=N+E~ zWujW~K0{&CS&iq!J?M{*AxeNz2=T^wyngVCfhQUKCW6L+I3C2)GaoH~&+{DFEC zXA3NJpo)@TW4@AO(0zDgxvI#Y+A7eUHSny4iF4Kq(!_65CdGlDVUsUQq@hr+gQPuQ z&ZB^Xh3$RMvU$KRB)T^P#|xKND_kO)K~y^PLIaC!m+l6z8s_Dd51y_nX8I9@$$>jz zRlwW6kgpa}^~OawW1-NY_t)UVCOP?p%x9mHEa zDbnm zm{!<74>E+nGUiDa%9VE(g)MT&2z^kpL0T`a43$>QojvN8X@TSz55~=(<&KEpfdZi3 zYUaMMWZzJtEJH2w7RYqSQ`!ruP$R%HLPcPS&`R|wbIp_9!J%t-P)5tdtmG~&%tHX@ zn!7?sdA=uI`;O%wVOVecpTwrHV5wXA-`Yiq)w*wfkZ*eRm1#*8G%i{QJ@B^h?vqn% zVymF7!o;&GQQTyYwm#5C$krB;^z{S*+lwG`!2(xr;3hTb9;-cLZM0H-O&o4wr@**@3NW?sn!FOH0ACbu8$WE+ zek?_bd53of;xSMXLMax-Wqm0?QNd^-wVFR~xGALn6ab+4H(JIor;l#+u=?Kp)jU_r z(Wcm{ZUPVam6HEfmM9tO9nyra^ztokQ&{-1uLEYc(`wDUYegE|8A8H54%LBT2GOi4 zKB6a3%JVc)ni$!vEG?b!|1pE5*O7SXJOV)#Xl8)3Z{$5+Tar&$at$Ls)OCM3N|Sjm z;G)1ZbMES-f-g=uVji-99IDscbe*3GbuVj%64W>r+j~$zRooRpc)I-pJl{u2V+rb* zj3{~Aq9r|UFuNp?Tn+gk+DQ!lz`YZ`5@z4hF)<1QlZJ|r`{-u%3Qpxv@QZX$#b(lC z)>7rB6d{eQ+s+W5)^3mQTVnD^&Nm%{Xw=~M65ueHU&{`QRa|ljSny>(7HjH#zsPkL z!eqb-(58OPKf7JYQ2}z6sW<{aBjpjwDxUM`-|G9hiB)j8v9?|hUND5I@fKVGJCP?Y zs)i>unqeIv>ccQA4$vLFfvW$gGy9|^@!7Y*&Dm)1*+R*Voay7I@$ISY#$9WsPaMp) z`4atX5YP}_G>dMo-G94!`27|&q*wMzJ5zFdsRl|P}nB5=ew_=<&a&moEcZun^ zuhY*0U#0x=W$+T9X;HN!COC(kH?uV%X9YY_hFET~geR6T7iDFaMq zdM2q18kg8_vN5ozKMX8KT#*AvFo>1Ue8?7fs|=O9m6?l`FwDYR)lAe-BIdPKCi=t_ z+$#u_iOg28+KP#Y=~g?$LuE1U0Fzki5(C)N@&JmBiN0(132~B0BtZOsUw+WuI(Rl( zUKcG@8emEO9(HFzKTfDOJj5@8?dD6asAd1JSw$CaB@)W^eend~1~!?QHn($7FIZV^ z(1gI<{=Pd-*zcp`NjdI{D4Vq0gfAV;)-}+77N$1?D!wkDiMj~WH*F|K3n?(7DA7zt ztZsW^p(hIG?j+kF*S0q}qHimFfJdF`jJt|PEDZO~lA!JsSr3UCFCXds3nn=LOj=5t zbUYC+=Hl=3zZOS`)0?g4tDCo`$CO7No=8M$47nAg_a%>F&6LFPmV()}$c%(1k7SIS zb1Mz7=8!0?CSB7ma*XT2l}CE{iqXav)KvGJ8l7+4%w4?2++$P5ZCxH3$P73ZGp)@Z zr~g!i1+s*Z83|E$b*}Gkh#dfr34@9=O64iN+alDSb|^9EK55Bi^A*$kS){2%4!NvB zr<5}{9ZD;NO$2r@T6hM^_QtKoc{)oaAj3i5yAJBXx$;iglaeA=wUU|_Ea6^fKqY?h zvAAZ}?$ireg&?nD%x~qaw7ikKr6s#*Sp0al_w%IWQnrR}lY1z2cEfp6Qzpd*jvS6s z&+c2*Yh-=RYxC!(?p&z4<9U{&vKn9LZx!+mDuN_UkLHq*S@14F4G<&fo`|vjz|Foz z9=?URe6S;&8W7W~{8!L{5I_-8zwl*ZDW@!jYQzp~89z;#i_QExoB0p?j6Q>viNZ?$Ce?kz|dhurrTY`;HhU%yGV2?j7aHThR-kUn{Qn1m@a2VPT zOnfXLnbd4K>mwRHy2XNz=~5z&@~q3%t~wsR1&YzT(gZU85=V%|mosMv8@WIkXLXr7 zJ2mb84yGUE5I}-WpnK%?Y}~ut*o-*7U8G22cV8R`GB`#LJh;&NsxW$F)QD42hz~So zaLI%CXA+>^GiQZ3lMP1M?8B~WZa5WOc$HY%{b}{c5%jEyyNRxr?*DZI!d@dJxMcaX zZLYj1?eCQKXqcoQCTpYF_ z?QjV(a37_w99%*^@9E>l$@??bQ7mB6VYFlJt!MX2%~4a4kBl_!tt=LS`Za%nS^O`9?#~?2zx*)x!pm&MG{g({G#D!K9by$T zq5ZL-moVjYk5<)xo!kiZd+Y6{gjLQz{RG<%LDI?*W+1D2?BZx^9tMK-r^w(@f9$^= zuu=!QP)qV5OWJw-QG@yVs!@ey-Y4{iMWe4&B>>C;qYKPD+X&B*qac63GVFA3pWP?V zUdg`(J1Sops^(Ur&J)|rxtKT*j(-xKkCl6^Wb8lOLvQKykP$@#aDGYt)%+&g`AFVA z6SV;#-P#u2y0#IP%5X&o5i$`R+k5w$>8VSc@}xoA4rf4oiDXj-iek9*oAz7rQ=kAq zXFyZS^P7 zpi4ku12gS?u$J3ZY@&DE4EFYJ#rivf4IFVY$596%Xe736dPu?K-?D!e5BzR?pngC^ zs|IK8uLb2n)n!0SAlTa(;&E^TE5B?LgK4ZE+@m_|yBZ|&a#P`fHbi152Y?5K77V$0 zI-ptfHp;=b_eWjp1-Gc)Pb4g`O$;F3J&5}dJ2FiV$ii!gNt2GWqCQ!Jn7d{Un!A#> zTXyvUSiRR)prFCxlsAZS+cKP=cdlUg4Z-ikCkC)XYirQXw&L14Ce#jesN^0EPGx&= zPlDY&5Lj$f_4f~f6R!57w&4S_ZeVKgKPH*sJsW_-ih}rjOHdv@C_epy$=yH%8`dwz zeBG*QdGr}i3o`^ste)%gs}<@a&c4Mv$8Ke+%W!lM)%LuvxXLkNSb47i^9dO%7|8AG z5&}_d`{4YZa4mox+uo|`aj4{7uk8jJq!5oimJOpeeL!}>0W}J7)&fni+B$R=eCS0x zZnwbYJk(6am-yMx-F=%KN`mV6)WM|%tNBmOjeVaU`d0^YBIEIbZS#UuQ~eb;+RP6d zh4^763&0q|;y$KRDD?tl5f2o4$+&1#3?#CFI42Wu^lHK_)O zT@C~x`JpPE;G;*OzzKlHyvq?ri0_P2^e{_i%@ica@!t;i@eJl8PW!5`^zyUMd2B&@ zvK+|+6yU!pNC%-3j#2~>iBZRemfogoe&@8WDncpbV5&-qkmEDORW;y&M4_quq_k4_HiqgpE=WVKAxyx# z`7IfN)a9d0@*d~~#3s4l!CcSg{|?q}=cybDJZ#|pzmqn4-uDb7D4=A-CbnXMIb)^1 ziCR;9(!yc^HaIxNTG7veG$GgsN6hkIz_xTT*=#l}+M(`M%4{ux?k1V92`dqSvnaSSHhB`aJVYfTzuumSnxo9Gwcdm}=NYLBEjm09w4gNob3i8<0Biha zB}dg0h>HoAK43W<)JsUAnn8Ss(%j@ncXYWyup;5O_&d8Vh3zf9x&Gp3(|k`)u`(Pf zh=ci`oDm1=l$*^3aMp9V)(C<44fcSGnF{P)Ay40>&Zu+TfKvTRUk3nA=uh*6v911b zIFq~5Kb+h2-(D(rM1hqsz?>tSZSUaawKrUj1Pci=d_d_UzlqY;t`Y+MX|Rn$2}lW7 z&821${iG=TIrs};GQj$dY#7^lGK`hhYFq5w6g`h%y3t-S_=mOwD1AX+I5DFV+PKGX zlVd_;k@PpoeST8()@1}Lf=!H#vHfj(=_ge|8vBp59LGd}C*UC7gK~`wT>Fqq@+=qZ z5L?q5HrNCISg}yQi`uK<*a0~6CAOD&cgB|#5ecx6f=QVFK~^EpVY!<%M!sN0F1P%E zZoWw2`73!^wQr1nc`tBtB!MGBv4&mc_a|!y5u{xM3PP^uI4G#}MIC7{S+*Ix(4%(e z;Rc} z=7#Qq-g*6j^S<>){*zn11f^_$6dDqr>6#4YqlLFRZ!0a+FK|*re+9Ul2C3|g<5y}0 zOmS8UYGdkMv;PGG(2exC&JVywxqv%ZSdsDqJkF|73x+g<(pz3_gF}NMV4`JZdFbW7 z5Ea$HUkMm#D2I^mwB}dFSrdf#?+=N>d;ie5Ysl$v`@rR1)|QsaIRzD;y-ol;hz&3) z|I#;Aa-al|JO`>e!tv@gH)aLc{{nkGSo$Z*BVhjSI4;saO>SUlNT6;kijh*zSpG^i0EM{G!b|Bm8`;vbbnJwS7WAAUR4bHh5e!sN{~*3!h57f*Gl(nL{ak5LICDwJQS3*x z?utNmm~e$R0k2za$J#O`G@iSl-MG3xuC#co#pZbMkigRU*u>Qp_%15+PxG?aEwp*d zQFNfH%w$jK z$1zsUltda7a^t##CBK3=gfwizNdUR39+dB>qCS$D^dp*)qcv;&IcSCeJ&`T|pmFpb2`4T^fqGI;#Ojfk zQT-Ns;u?wvd*=U)wI}Dr)sc35$z{)F;gRqz`O(MDW@!|O_JM;`WAu5G4xyNzZ}b+bCB4*x(5&= zQH}g@Z$tPXS70GjDfRb7pJ)fDV@tet^u{s(4aCcBD=S2e9~F3f)b2hBwtUDt$}bi4fcpni2c)L` zQK>qBmRWHx_4Fn|iqM)!O`%a28W==Z^aJ|V>g@hAmG!q%7%7;cVc1SU?eNhT`oQz} z3mtZ!uvvoy|Htz^KO=5wvg8Z&56~H)eY-t_JLq)xO%`VSny~Adzf_Iho!^TF`4u!2 z^V>%!aLRy_Tk3p7n=dnXJ_gK`6_Z~7Y2P3<$$3ZpNhH;rNydDnoMB{!K4f#jOB~eC z#?d>s%ds!^pqo=yLa$M`-jlU1aESgjQ&!dgZv)a!F}1hHJAcn>WSL1%Wk}AmW&ds1 zUf$|iO|>SEl-CXnmgMBoc^9FvGgUY^?~(y+W`+CxKp763wO|JXKjvg&y#iASX%%$X z3ExPt6l;MVjeraHFHb(>O{~M7Q&Ya$H|HCJe;35?h_|5%PELDkxhGgdtZ|ZXlFR>O&jakjJ&3npig@^@@6sv z|AydRK(DOSb%O#Dsg(di;)O*0lif?$}Vda1C&!!F*9IkASS{&<~mAKK(D0-y!|BtMxe(y&Gn-_=_+ zi4XVN+WHgwM=J{Q2RP!@f(Vf=8S?7|v&1*K+1>aDJ@A|(Mal2T~owc-hvhe$aq~I-a27b2e zKsBuN;TJkn!C`M+u?o|C;md=JWu+tShz|zL2=y=qS{aC;$|ZI$OTHL)C#G&$6|>-0 zmDycF(b$L8*?*MDUE23FGrP@E22HSBn#ipch+G=Ahx+qSzcizNZtd(!zm>&SNGba^ zq`!e??@?R+syYV{gDPj#j#?fyL75Hgiox!7mGg8;3={x#a@2?;9q4i3B{a8UnqMpz zLDywzsyc=j?@AZf+;83#_8TO}=%QDSZ^868WvWP4bPBjF&r}A6N#N`2RGSB^BcO{E z+zx-1eLyYo=i8{8y_Hx{ZTFbFi|#Es(F zkKj~0BsCTmS-4x-h55T@;`)uxt@$+IT<^jqgCf(;a34To-sdgwq9O83S=Ld7{*+{6 zLmM*SLx~f{lLOk6nDKQmFA*z2rTfqdm@-lzD8KfU_zIS|pyCF{7bo`!IM+)>d5U5l za?~lLh9q`SZz8R7qCB+?Koow7EUI&$T2dV3U{{mkKk!_BF}U*Gv=v*7^xM`bV}fG#1}9TW$Z$|LAQGEx zWNNA&AjOVlclgOk?$$|BSESJhV1N~k&-XW+hi5h`!FvHs7c`7c&rf=Cd`2&QF$as( zSri-+t046C))0!-v~3?Ps0IPuxJ8cRj)qv_qJJ#zfMQz8D=~qnxGl$L;JrbeFB)Xo z1*%;zD?zowiWyoa5P1{k5s^0=(I8QnY!CH>$R2JzG~*tf0@HXYFFfC45drH6eh0w) z(!~7|S!M)C2i_2?m8nF4X@>IqJlscw>TV<04#2J_CTq=emp{7^JfD%-qmenSEf_h# z?#T5w>Rzt*a*Bsg2xHIk8w~&;snj7ZjtXEK>*5)?8f?h=jvJN>E1K8Sh93V_7QtYd1nMm#KN+WXF($+3Gx$8i$1gfy9vTzFPQ76uRM-Nd7 zRI|5Na*Il=%JSXR5!<+Nw~}kShRPxjd-pzg5Rjf8z?XFMp^}-_^MoC%AHk*gKh0B% z>k|bvNO%QF{;M$m*|v3O=$keVAJO;nC%@(<`5GE@10*;OdRc+X1_BLCJ`+VnmcoN^ zaJDli_{7!{ZX%9Ptdss_ihIZYVj3c;cW*CzPK!I=x}2Sv4^55Ft+s{|Dt-t8+-5kt zJFEsCp=yK}>>rtzgihzMFp@jXIy*nj-Mg0eYOOr-fV%g^9=8N4aCPO0AJwn>Z+%X}ze z%Ij5;P9D|tl~wt3;kA!VQhkLO0=6zUHqE>av*oX%4?n!Q@kPhAF=6S<%NCD8yjjW_ zmn!$CeLVpO@^1Va-$ul!x8M{>8BFI}Zbe?*kq$v~?9RI_y4pQEZf z(eFA7o8XN{rQ@YK4XRwPM0W0P>kl(K9gBsAUF%LKN^6U3|NVMKvWH8q&n2fb-&5fD z%?9uEyM;d??pN&|E(lnzr?3RQOwTH>?OM{`yYhK7x_7!tFu*z%|H9~aDe}npU!G)^ z7mkI$v4xpoyT(;7?R(@@%*I!$DCc4ZjO}%)mgastQx%m7yH>-3Y* z5|?zNg?YJy@w;-==@hG&*qBfKnu7KyeI_@TtfN`?BZ*7y_D037_-E&q^^Jdt%t_GF z`W-uFwF$0B?2U#U_jvC=Znsb+X$bNUZRyD}KRU9XUi^_Q^VhEXl!gV<)-UqZ&&{|Y zPn3h#<^J>1Sk>(WotuFq#mXFu4pJMPdIjnS233E*k2h>Mp zBi5M?ovMqihn;amOfoMt{{CQf5M0PT51Nz(em1k>xZUBEPyy$6Z&*pT{I4yCqZirO zpSSuLTtMy*4X+*MH?ksjN4p{~lpN1`p)}U<=Ik)I%-_kKcvh%*@5mU;1Yia@+{)F< zi@puR7X0#7Js|DZ-{19!r@OPMtu4{?ePHa!K>TNZ=tM&3?l`f65}M9ScA6 z2QB2r)9`W&80&6_EzXGfC7G>K^pMy)$O?*tCJzMPAD?h@?jb`|$~qu%JuhB;7^a0 zzqcLz`vngV35yH=evf7hHs!znI`05m`QO(mwaX0fk9#s!l(}KBjl58&hK>(MkLUc4 z_n?h}=|&^@1w1_YDc$hj-XaI%O1YVNd5_%pC#r7DD}DR((Opha(I6;?hi-!Iu>ST_ zTH2Z`$n=oGg~I;&b)5z8T2I~0xZr}CbXXu^H_H;feq6s#F`vG-op$<#d=OL9TKh}c z4ZcSc`irawMZbl4Z^wP&Wqld0eq=6ie6Ueg5tv(bR2FzB;vIE7E}(xm8at`6k#V{* zGlsbf<}DR(^In>lt)^AHQ-HuHv`E$w==Qmo0hyP>KEAJ-}l# zo88qO`GIHa4V^B#`VYJ_PCl^5VBafz+dO63K5_%|^@g8SSj>AVr}57Ua?idgm{%Sp zyXkL_TX@!>ne9L?MnpJ4-s2FalB4H3e~OnAMix9cj6aQ`;j>ofu|S1-!a5ZYeu>`ZJnu?l%jYx*x6f8V`ZRo)Och`f5(-X z_w~;5U5?tiY^?aDb44nI-gI>LRd_rf-{ELae}77`eMOdW2-Et&&}lfdyhc+NY-)F8 zWORfv+6z>KT#Ei(vn+5v5A|vYOH_XTod%}A({S^_ihIITiufSzDJ-*%Fw)SNPhTL? z+B?3aFJ=6$)ls`^n6Z1{&(5Z9x$}iGkV1HKCpo_}s<=t2@`wUrs@3h=o4=j(+rFue z_u>{7e|t-ZY|F#9WAHd>l>X<{itW(LB5|T9tgMOE=L5KYHpi~k%nwD3Ez34n{g?_J z#)bYL-rhT`$u0UC#B0HF6|sP*fKtQ;i1dykAR-{rl&YX~2uPD|Ls6PE=|xZw5D@9z z2B?(Kks4~~1PCFJ1Ty=mcjotf^UVA;&*XV-0g|_zv(MgZt-VfO<=B;dX}L{JveM^8 z;DnJs#81+(ey_j(9-QOo>xw_IIhVzaJ9bzZ_1}20Y0C#oU$WQR=A_S)btWz&V?PA= zOE7eRZyl$^uNRt_1&Qq5Grx9AYM6CY*TC)NpX=cX*tca*boR~hXQy4KJ4z00W_Y0S zEhwl9md%0G{lV%bD#5a!48zP3zV_y z`p}-gAJ+3FqfF@=FeJiISUS+V(9beHhW~NcL3j3-*2Fi+*T2Jg&lU!GX94eOaWOIY z`t4UdrzB5lg$fvc*=ogL#d!QWj;5|};C78k_QQwv@!73PaUR|F2Xa__3h58RPiUm< zg)=kCt%&MWJTz2cqZ1Bh3fl`*m118trK;iCr&M;)z9EBxAg;L25IYsqP zG*}wK6s=6vZ`a(EvYT^GK4q#VmER5{W#D=>Tx#ygHMFd*-x+%gXF7ObzH@&pllFEv z(r@Xk`A8cl5NsQnLR7z9c)BST?jWpA-72PMlMzY#%h9MCjX0DtY zMd^Lm#fa_e9*hX5L-sCF7qXl-$9vxgz0N`q>c$a zzGmpzc8&Ffs+7y|Vt1J1{$X%@`u_H7Pchpli*$jO%EFsFckD<%xiEZBG~T(&t<#@% zqZF%huDh#KE&R(x#Xi}P#9(zB2M0YzN5_hZ&_O~;K`Z8rUQQSs@C?U~mZBf~8NbO* zhxSqnddn$tJHlXe2Yf^1w`TYgs`4rbla=cZ`V$vqkGQ&;x_bFFs>SpEkl#2lU}_)` z&mT@ibNfjX#zSayzKxvsbztvwt$_PXAnWIz)e`nXe^<2{z8LQQ2MZ3TIPB%<3n5EcHstu5KjLQ(b4o8!Q4lR`{`r zt;Ms&tfwvRxFmNDWfsjerDW778u@8^lxAsOe3PJGT5u;s!^K<)r!8s1^y)ig9z&6z23{J6>6yNprPE@}I1 z8OoDfs_@hJJ<$@jr{m*v)$sBINAC!j_S$rq+8bA$*SEB;0{R7Vu1C8=o250w!lKYZ-Mcl6Wq7{O*v>gz znG0oZNR|p|IP5T27}$~#GCJ3l#pnNIi~FHS_hkw|rs@sK5g)zvvbvCvDG^8avN+OR z`^08x!EZ)+LXRrbT8ULZCa2M3YCg%MAU-RV|-3u~g9^gzg~Pa}#$;+Kuj;Bc1xjYz11lmS9=G>>e# z-Sdrd%e)tAE2jXNz%aj0F!s|K4?7L5`S%8C*sN?`4`p9ZLEF`oJ2itv1g)DM{ybPK z!b~`yHLdhav(+2iE@_j9u^pa*heR&VbYajn8YIY-3`{^yrOYH%NnLksJlf-i%_CSj ze=`m2%kiNyO^#~H={8N6UcMgxI$>-_sL7gQ?jGHvLWYgXN+#*2BJym7zvtv+cW3+E z*A7-!x3ODpY?XqrR*lGYDB`axN8i+6ZlHYvmjbw%P?Umo>Ms!F<`!gklsAP)?U53L zSq4I<{FYIMJ2#m5`=Gd}#F6+$$Bz3N+U=^4=B>xGMDe5RLI#y7j(G@2Pu)XV1*V&E zY+^v$R@Oc$I7I}&k1VkQ*D(A}3qO4p>IiwQ-;4I6$inI{tF$t#UecG4ZV`D|MKQxW zHC3S_Oyt(i9Wbv8&c6$@0%xDSpvZ($(Nn{N4pj=EV*mWnWa~WN0(?hQUiK@EzHMR$ z)YC}%M9s;wXLSI(@O}#=oRf%pGgVWr)zjxTowm+sz?k8mG_>4om~qCduUX8H@`__& z3)`vojLYG1@imyo@q6V5Epxk};G>m-E%qpBbd|7aY|TE^?w|+b4dq~_AZU!O)k^T> zb)(rsIX;wphpj9SYd?Wti1wcbWCY9jBAPr6ha~*lCL7@tH!-nT=PFP5=2-2nh%-LZ zU0v@a%;PmVcTY%|*CsaaT9_5Bg zxkn|qD+l);66VaDv7mb|o^&;mGwbMw<863;!MLJxA){K{&uoDWMa9S{!Trk?-g$lV ztFgygcs1v3n~s?f!;Iui$W3|LtnUgN+m}QpqH^$Jm#;%5p}<-#Gff{9YdESj zQS}~<0_+huSv_Hf`0cRpHy^L=hSP%)d0bveEP@G^T>yZ^qQyg)(=@Ph1qMnhGHu0adE_=v)Hw%Mk z`mvvu-|_$LW;-CF}ZQLHwm&)SQAUnlu;6Zw+ay^8@~^`1Fwh4YGMeUWu4T^ z0i1$U)UpU(Yd`CGo4y9t5xIq<&U>JkMrwC{>G}~{ms=8vH9UA~$zLoG2*R#2~>aA|`4*OcQ|%9^Sw6*Hfj~P3n-A@S~6|V@)x*r366k zdM|5J0dG$~Z+uok>K!C5u{o1eaad`oHHs~d52u9=fW)#Op(XWjwxjn;_=5x4(*O~) zMJM|y_xUF~0luO3U%_I=m( z?@QrmK~ckgcsD9OH^1gkJ{D_)BWfhPU34Wde-`k0_#4m~t}F#@7gg@Aj;v~ZuL+k2 zOFI7cM$ljN$NhA$T+Z+x4;_*XCI`e;T! z>I1=MvQ6gQdhMYoBcKXpg7C2>^TE}%1&jW%p`5&lXp$aWHqjD2jqd?HRacNU2mrgF z>E_SnQ|!%_ydJ%tY-#5-FtJ_6Fi9SKzLxIK$>Qb$c`;$F>z)^YtNMvUOl0GV29KwY zu`$~u6m)d@@T%H3&ye@`eV9+A8J>m36A2KomUCX%(ERYQQsZ~4WO}tN&irYGa)3?H zl_>ANSA$9CN$;B#gNAu+J;8xFJB z%x9Qp2(%P{k#X+dk2eg8?ubEhyixp7S9-X)S3LO?cMIpciQu^+qxr^`cMVeB@AES= zHL){uTa{pnvfIaZ?j*ZWm0wiTq(_@7-3uMo@j$_hK5B*^=xW}5NV#viT5oxS5wDJP zLIxV^=TrY&fdmZP(AJ&fM09qU?o4^X?I*<`*RU^qp6WLBOG7Hq;^l3hPcbl5HG)FJ zYT~K8)!)kpUuoA6M))V|wU$;+HS4~8XKhQ1+FkEg2zZ3UEdib|Yv#9kvbRq16Og5k zPYv%1o;+_j1%t{BPi?laKjS#v>=AH$yjjxH7{^l5XIFJtn5$g(oc)tuM250t68pGt`P1i3>|E>;C?R`{*g5_l)e-i%y4sLdMW(YlH>}

blZEJ?TyG8StsMr~UIg(;tQ5ZLVHt^q&7h#>-;0G$lql4KOLf z2Fsfm9t@64nAmT-9nLp&-e8m^de-*fdr>iuFNG^#wt(0b<;E=$ywRfx;ayQ&I$%Nl z(XRO6&GY?{5%C$l<(?qA**WNAN-XA{pPRSp6cQFOEb(=&;2r?yw1}YnE04$V%*T@9 zk%i?n1J-{JcTAnHlPi2`*mv}G@2lg?kMH_T?4IKnXbsUfjsB{Y24>kmG_=#t!N_*o z0F;xDHN>ZgvJ@qV0~>Dn*E(ldFPS-87BO>CLHm^lG&Z7xYXX!P$&bCOCSI>I7l|I8 zIh7Mf=ha{tX&cSPXgnsY!#AD9F&5PvBJME!nqvTJG4`G9=(a7Gy5L>Ox+S?44W0eN z8#DZMGoB^Q=qay5$=fxU&mnmXqSB8XP0IyFF*3`u9zb~=L{_0ZK zSH5LHTw~3to{%*t%m%{xv4;zEiAk6^pQTL2a7&)7lH|~ei-6XU=qH(xDcawACh=6| z#FfNov6=U#+|;euNi~p~o5IS&b@WV*L#6ZzRBJ3Que+>c&e0z7#Q$j=^?gAmJCK(K zlWRN>LC#w+`cIy0iMRI)cCLp-3{ZiTF|buG=dt3&N3M5i5+J*3{N-4GRkb<6n!%jz zxgcgPw&OH~QCV?9BU?!cRTiU6?Ji!iG#oZDJ(o_Kyz2C$TA=B1uW1nW-7{g9A{*U- zuI;k{0f91p4Ta1v-Nr2o#J#5_1Z+!5L3j+BR!4`mO}q*zhnRiJ?20c(y<(4qiJzt- z03%^@r$EW(lJwk%dv5DmAX}Z}6UWjN2SKMm2(GK5eT@_T0se_k8+S!atVG*%1@E;CuML591S}0m%|& ziue%JZ8_ey>ZBiIdc-*&D0Xl8`d!&D(tD7fRI4Ap6H>vz+Ul~uZ}JDz6q$9#?ddy1 zW*JT2KAP?yf+W@Zol%=tgarXQs3y7L>K2iRXOTW&JHN}EQ?)whxn=r5HV}$V;F={> zEtZA8rl)@u=&SDMTiAsj7q*>YOUCw-%F_8XdHUN<@%RWgvNM2E-G@0OIEeL4j|=5$5&w z1{jSF9dZT6SDz60a{3m$GiF4ar|gQq?|Ce1&XWw`R%|BPyEi&Y{~gGj$>YMwH_~S9rIFN_ zljKKH$HbRZzJp(oYNJglXKfQ0lN?$%hu+5z&>W-zCq;$@ng3dE6ASYt0{_%jwVb}=NtJ;3DPU%Y}O@6J>wPVEX3^~W0$3wu*MwEu6rETc74Zf`Ut|Jt{;PNO zSlJ;YjsuK_Br2UC&H~-GxM(p$wg$avfA&1pr7iDf#O=dC0)fmB2_hK0tq4|aoY>uu z0Q<%I0xZrNo{Hzcj^qO*1y2hid z^`oghV6x#Lvs0LI@X=f#JR%jhcwjKETVk&P2h{N#O7Tc~C6YP%A-8r;tistvjBauH z@&`#-#Xo|Gz9QGQ_&Lvs49gfI7EfOjM9Fq{$#gW%UJ>4h4zp66h?1jIzPSi+5F`r3 zkj^)Mt&DDh76*G?buQz4jV5X{ft0NSnzNYU8#dqddu?0h?vq@JN3k(Uua_L7JWNU* zed9bdERJ%janN#3nEC~qDJy5AzZBe>o@JAjqTjtRY~n0p>vvvx`w=?nl`kgFS8jf% zb8|NY<1*1n-wKqS##{5CD{X8rnFlJ_Ci`CX8z|66)jK~^6?(o=B2&|P_3EZ4J?W1H zwm$hw>M*~V1Svj3V2YFgThLApX2rfM`cbqw6&B2zXuzg~2O&cQ*J!_&^h+IL}J znIVcU9Mtt#=G5JMjhY>&YS?-UXZt*C)`?s3JuK)KpS>s zv&*hc6X9>3tS;vGy*$f8e54B!pT^_%_RVU5n>$bLCXf!XiUzOwR)U~{Ihb%br(c8q zxTb84+Iqye>XQ@%P_oz~P$hdCjCxEXEa zoevu6DG<<%?=9Ari+QAW$*sq{0x2I=PQYsrLT3crf`EIyhHRAI_c?P+I>XD0J98xW zkomSS*@Ml9fD&+rE{MTdK@ml1(6)7R!DRrFTf8FiLdo>dOb7D$Mm-b}(nUvM-0Ncc(V5=BF~B3ER_fX5sRf z=_Wv*eWt!urg-;%7>0~ohW+LBK$+>InZQCiA1&CasK$~-yY_aQsmp6kiCbI5!8uS+ zmBn}2(0=gK0hYryCf@`BopyMrVcVP3;@?U;B-%J&44+e0BWF6;%jC)GBF3&3zFSD1ezw3*L8rX=|VFj`r;sn zTJe2!O?et$L04=;^z;T5KaDY^MQbEd^gzBUWQ*H!Q-)n(zS6b4`hAY}y1r>i#ru2= zAJ4aJSYT1lvhN&WdHemlT3^3j8Vl4pcvHmaebWVz1SYR zu)~KBcFvRGy8%=ZbIqX^#yreyV#$22Z~9hEp!!C*f&a(ECf1cSVzVtN1`H*J1P$Q{ z)j$wZKz}R0)pE>daXcD78Jzna>;ldrL7-CssS+8fkb;CpD%f*(6T?D7gk9j<_CN}- zAok*N@*am3XohKLRCkt^=F82vmc=#U$ODO^=XiKZ+ZVFW5Yo~JUW1BOC8l1NzYPiR zROD1;uMEaqc_Wx02iuY0MOSuw@!*cpfyHWb4>vbUvj1h^tjs~PfzNElS!+NsR#t1o9NqN_! zgoQHurex2t3hAb*gVD1QV{F!*6q$>h9!=|=ox8S%FJHocm3y5jRC5q=ug~B^4YQ>K z$pk3vo>o6DzIb9{+5e$?ptWhD7u&nL`^1_|S2wrRuASuR;UJ|%0sYlMDir=c4A~zp z-tSj_ZN_FSYBFkK_MO^rG!1PcApASjuYjgt_pYcW7VBb9V0+T`{>ZZg!uuV;lWowa zlw-YyIwCOdJx`UVGwV6+UB%1YhpY>iT)_`y)v|^^4{PwF>1wtAJXlLWa{a@Jm*_NB zXbOmTn;PopEh#=Tu(t&}#pUo+EdVYZ{a%-C0&#k#0gfY>*102CXRv{j*L1s zB~{k~Ic}T)u9~J2ys-9lFviOg!U?#H4zvL!>$7-dH^vNnu6;Cx7Od7%aLM<@o56re zUh<%(A!p>QJ_xaIRFm|KQE$o+$?YLD@$4OjePycz+3CN{q!Mc$p;jIG;+k%>>#B^4>b~p{OqrV_n1eXk{ z2HphpQt|_YhZ1UMfFTE81G3$|xK!+3E>bn<%2RFXiGT3N>1LxpVlncFnW2H%1Qv!( z4b)Ukefpt63T>P4qnB}HqhkJmgwt z1`G|gX7cUc0{nxhAzfclZc1>ffZv1Wfq&j!u}Hz+wtpP1nabeOqN?WHlyyaF?{7aF=eE&vxBA{?pKnIsC~=f z9M56$37&6ffVIilTk>l5Wk3)5v_z>ZHDe`d|DF7k&TH2P{BTC1A`&2Z<$x?ZS4;%u z%^N>1)a4FBMAdrT9%zhaZ)E3*y14{l#VX+!psr9TGZ<89U-1~n%V?q27qoAuYbXYa zX#&Egh$ZHjcsWswz#o>Lrdz)ZSzj9r3KoV%AaUtd@}a(59GzOzG$8{m9?((4dA2e# z#y2>C8V`%F*VG^vYLw5sow#ykM^|TjA96X-?-fqRcsK+MMu5_$e&nN+aW1GwN#DI= z9+6VPr~p{-jxTcF9Q(94;S0f4L{P(v)mJKBt^P2}bS$6ijgFgD)%G`ZDrUBWers=f z`Lbg?cIsp)yXd|9(Iq))_C-(q-6TR!v9>ZC`v5m-WVDTT!+1d7B7=E31X8bBICvep zhVRwMyo)wj$^*BBUq;o3QBm21{C8MWYQZ$NDbpBE&#)|OKmu`lD2Q!fTTrVN1FaqM z#Hovd5WJ*IT?L@oDrXQ`#Kpzc_7US(QpJgbVLZW{?n=ouV)il6fO@c@glt_8gjryU z?otjoJJ>O7pptU(q9AIbhQBe@nD)GdzbC3)YBF?B)D|QbPcGuH@6?-X*GlzOBYwNG z9&B9veUyUpcb8m0vL1-FKe1^Q9&;K9c`h96e`d=c239U2gA#NcjCW209eM>t7lZ-- z5z;xJnfP>i1tP>5GE@A9B{gR)9~ZQ{HK7~yWI~!VL-Pyh9*fz3M_ndJ9XvNS(OLk= zBf}9gN)+cNJz8df1F^_11pKuY0Ax0cPNq86aRCEFdExw0A!8{hA@+hD>h1GwE$>ds z)(p#2XrykCL4Yu%p^3KdM!7qMyhRK7+Y)k6mk{KJUf_d(5_xS)b=&=1(*(*^M>f^W zL6(?RNZD2QEvO?`9)AX8?C>{0Wkk3r_jb1L=F><*4eh8X_DIks#S9}kO8q{w`LL*8 zt-Qh(dS|-hxi;h{gh>i@=F_=#>vfEYWmg*ZOw)EXcN>@BQj=+Y=RlyHB6gSjNdx#? z;0g>BHZ&v(tO*hx;ao0UcYB+Vc@JVai?X^O`G0KWaN;Q?lF$XM<)!}eP+;91{6Yr` z#MHOv`%mXdyboQ8z8>kYlilT^K*j9!V2=H1P{SF|FeUeJ5-TgB+qOS~W${1&lo{0QJdnBzgwFEwz6rTOoGO34g^ta)LjI>om0{@Y z|1hKe^+NvN&8YCUqd8J1R1fbHius*n;dH+}D!@_BKhxzXNV;6fz*Sz*!G$g^LA;Ll@wW%i$cx#ZMmnc|U{i*?@hk zYkP`s6oMUU889m}&K1KC{As_teonct*_Z0xw+nfwo^4y2pu)P-^>}jyJcGddk@XeY zDcUbtIvOUj6C=T_4e*66wEl^$;1d{ITR1=GMQm zv)1Ly=cExPh8I%QHd%n{6%jr@pobVpjltCQc&Zcn&6=H`!m#{C3fftuU)Hx4MV zG5obzKGTKMeJ!2#|E$Om9lM6VR~Eg8N+yJDeQ{0o)~K)I*obyS5k zz$5?1F z*Ln)%ht5u)w*XWi;oJz%IP!!CAUDP}LKpBN3hLd#lxC%kI!>qYEs%q&XQ{Fu>M+Kvzn2=dRtI zBg4;cN8*>kb&9Vwftn4};kRRI zU0*zIM)+b|d3m0Jti^lrvm!ds#cpy{MRxuohjokb%8#8#d)lC-F>PPaI1WJrnTXjVgdM|#I< z=i?Z~GK(2Cm5*F-=Qr#Eu}TCuuE zzQ%+lm30K|stI?g&(Urwu6+IaG1?Q*G~rRgyNw~G#&~tqPYXKXZWC-E88s;Yel(BA zVzOn|zh&p0uaLTP*0AUFH>2fG+!hQoRW#Z%a_X=3 zfT;Zxm?6xTy|3V1O5~miJ!x%_{TlQ74BTPM6cpc?B})3$>ep%wU@v$N6!QN{m60H_ z{ax=0j)<7NS%C<_Gg49 z+>#cW4RM2f(=$C$(HH@G%jR88AqPP#oq~4Xx)kor@0)xeZyut(B?x3`v{M2r5B=JEq !#86cdoi5L`k@Y}sZ-7t?wqexSSPH>^aSK`sV3=cM$ z-(MaNf5Ylli6@oogdyRhR73X6!Les(ICxP{aWVWkbyOCRJ%c3FlFT^mMN*PVyvZ zG8g|e_r}0U9CVuh7cq%v3ig0RFZ;y%fRx^N?i4(TDMA&;GeUlwu~+ z|Jrt-fqD`;>c;1+UP^M|VQ!%z3SP(z6nUWBAcr`31(eZ$J%ma#R1&vA69rVE^RIF` zFf3! zi@JNilrH4V@HfzbMio-E4f)d6+A1f6)zF?XOD_S6)RBargT>mgi6@dYb_JTQaV9-= zXGZI-fjOftD15Bxs&NY1y##NAng+&a7ritW80g)~YJ*6N0{+bnh5D;>YugbU0OZl8tlmdUG3^$q(`MpQ`Aw0c-Il&gKEa;)?fWrsyTjSB)_n;Sz>2Nc^TLzBp{sqMG7WWbh; zD@*;2g@W}>(I6Yed8pgaP$jQg^+v~E;C2s^jvm=?wbzCh+uiw z4;KMka~h)Ow~^QdAS`Uu@*HO>%TaFmJ1!GQrUI1jp3R@M_VsptVb2oQs|q%k5j`6B zU!iP^0K}CAn*^X8u~s+}@diX?9@XD|=PLW0lI%W7Fi$?i!B=_6gGNWC`Buu_&FZP! zcxoFV<+Fi0s<+O=9;Gf!FBh1OlJ!>4aF|RJr8{p7u38rMmOM4|dKJq5rkgTS|w=at$h_H4z8nZKnRg{#6ryo z90(B9uvaPVi|1?M$3Umht~{`uAty2X5#|l%vUS9tSa@&kdU9A1$i?X0Jx_lPzXMaq z?;d}d_bEo?x(6{v@b$Oxt zIyyyVA%#zDdmB=&zUAgw{1V_GEMd6VBrRvI3|hS)!v4x37lF$e>XvR`pS@fc2ebgo za@%p5UZg+6@~H>*w^1H2CshMaz?*~rFx46!^)>-DK=!>fKZQDqe>-1|kG(4SCk;KA z2T_3X9VQFe82+Pa3=HQOV4eSY3;g@=Z}D(hRG&?Wu;$ zg|=70OMwx#gdJi%4=!vkXuIC6`JfFGd?1Y-V(SNV+Ry0cXaR8pjp5W!2G|}m^EiAR zdBsq&1)n~*(w{+{RM^YAfPv3smgAk#USnY6Ex@a-TepGY2C;9eRd)|HYoIB95*{rT z$r6a#@*IBma_||Jm&3uxWChtr;E+hbKcsxHR%bzP1$6xIH{eS-1ybFCKbM=be@n(j z^<(Vfu2>`@USe2K+`L8{*q#VuiE0w)Ss-ZF1*;fKOnir(z`)qhhE8OvL>#+a6Nu3a zxEr0?QF|M1Uwm)Ewl5lTDuQ=T@bPAq-rZG42+v0F%41Ym(& zFGrI*t-My0A&qJsh^eppU-hDVlO0SFaL#86vfKWQJwt+F%xDJ|b-+MGda_MI9WV-# zoX3~m!bXp@-B|nKLtE-OB>sEN_koK$>@RX0(CRlO<7q$ zYO^rWL#E3t#|djGM=dZ-?%uHLo+!f++`mm__~;1z%IMdos3eb4zo}&db6V!`{C$m= zc|Kc<7nd(ZNf7#--}lvRk|d8>*4rKtoBm(l;+}Bow4{P%qLaaU-?f9X0fUN6zYE)Q z9*M17Tq7}Qp?X{X&tsnb-7$cfd3paox#`&A?|o75JB5)QXdgMe9(>#X-yZho-P${b z7HsV7UQ9$sD_#1=`&jV$%Pol$yN`(1PXE+T#o(CgdMuL$@hfE+%b^h(vqzX)hkdSV zm7NfCpm`;@;;o&`aQhPq1w4JMW@K0nX23Ol*~EH@Fj%#RA~86Jt1)~=T|=jRaA$AA zy~}qWrHMspB;x}tZrow723>|9}=2QpY_wM%#wRfl`aFVwo5@G#4Uwcw%F35$uhKMfnd}qm8|Copc6jOX zuIj!Tzmh#<;%^xEZQVr6|}Xrv#Tlig}gap|N6W?uB5j*?NNL2@*MRuy2=FCI_n$`Y>m6-$>{h# zK>^CF<;Z(s%|MeX&)T62n-hmLQtx6b2h1Nc?haz^_7T0OIpy~&RJ>if%G0%}MM%8F zR7W?b`#zdLg=hN`+J;Y`?*0~&@4?LMG*~wA?H$I1lk+}ZUM0_@Y9vaYc4}k7A6H+w zw#%&Uy`)oPt4jTcGu8dpA?yCErY}a6)XbNIaRC7#J2XP2^^Wz0B^fL<%57fb!^`^o z3Yi0UF3$GFV8Hinm(nnm$K`xJ^A&`0{=62wS(sF<>U56|5AL<|m?Dhu6(k41!P4%x zwyl!1otdbtg5{osg#MbEKkE9VqvRC@l`I&=BXJ?+!<74zD%vA(^w9m(q`f{N(qib) zqZQ^g2XX7olK}zu9ebDGy)!Dn`sj6c%#&`Emr07*^&dDQ=J3MBnYfAF(A>P;x5RHP zN`Bs5o71`IFHMjM>C6j!YGQwa^xVTvOZ+7KkW2bUfq}=)QZZq~u z$NEaQ%rUvq8NL3Hnz_#b+EO~7`}w%dup)uf!zA(97lJAST{+Cmix0_k=at1xtW5B= za3fz#>&Wxvd?sg2*!SUP^KQy1E3rd26lNOYf` z-+N}Sy101#2rst2kW)U%XTGtxux1p7R(ZhDr!hv9FRLrp*15dnYKm*qhX(TW>!24u zMHknfljlX7mirPX>gv*0dj#?IK{uTZNZkQ5)#csQfiJXsL^X(nO4$<{)is?idHmFQ zh}DJ-@)9q$a60>uq-LQfeBqy5J^8s8u?B|^=t3D|x!y0%Cl}5?`ph6 zsr;c>%L48xoM$u6)1+6bo~=`~NNB!zM^3J!PY7W@U3N5VJ;bWXNNr->&cbb=y=3J= z!ypH#X{K*BF(81E)AuaC$#lUY*+f#L+Kn{sl`|E0Q+_pvN^tg0Uabk0X~7@r6+5zN+_+CgS!p+zb+`NTcb&(r8KcNd@6?h_Let6k6% zS0cTdo`i#yE}e<4eWoFGDt&!*@m-by=U0`1!tSQE4Auj6u#mW52^y_MHK3&HAo|wA zX;?7LonYeBpI=}1O?nAsqkvz+1}lnui3cnb%PjeSzS^W>!Tz(c8pVLV-+J9@?QDI) zYXft0ZKHM4k%D1Ei>lACdPcq62hy~pzJJ1cOIb9=?cbHOrta-NTe3n~*R+s_FN@=( z|GH_v;d5f_=ONdxq%Z@$eY(0c1ruvie13;zy_N$4WVzp~1eIIo%>}aZH&wH)jAu2} z9M~)%LAR&o@|T3vNK*$7xsA6-j24F031ipA&;^;-;cMM%gv&dZyZOKNpqa$@uE(wZ znB_BT{8b6#!KvOJq9K)!xpc`cGgmrgu5af)!=FZkW@&PQ#C)kaTJ!G)ud~A-bxeCY zr)V@xxsKsi0$(c%1e=G*N%cwV=H+6XljT=i3-80CTqzf)>wD#967hloZ)1~HI~#3uV(#Q&9}AS9YnGTx6QWLND(iMqP~@MCS-hMrC=ygL7h)%9xgu}gm9V<; z&1-a3uHFjJK6H4#?$DSz2LaWS^1-ym&qJRYC+>IQK;wNL3W*v$8@!ghZsxm^?%lww zpIou}W%gu7GJRyq?v@_Z7$%K)4rXTg1R0?t*Z-{;Y3MmOwKjg&>BmrDDwTUFpm-% zyU()okRvx*CsyTzFdL5!QRj{nOq?>qelNbdy;E-}5-ORsysx)rsF9?IGHx|^a-c## z=%|x%w${7AXqjYo{{*(%J}9NXk;a6|)w9PB$O+#iRp057vCk&-#;ug3-cuK|qAY&Z zF5uoG$mXr}{-oFOr4v_QBscnU32=JeT%K*^K71tp5T(R`$=wf9MuSO&h1`ncO4aXy;J#vL z^7TA~O^;2-^mpd&+JGZ9>=1^)Xb42g)d6c2dQa8iz)^65mV!tWc8ob zuWa+G&-*TXDy&k@`Hfs$w%8`L-QHO0uDR>yz;>f*|JU53hXGw+ijo^U-Y}^r_S?k$ zOp^8dhK7^b>!0pMDUYf_>R=4HAU4N=V)<|B5G^1eW}V1K_BxAZK7KV~zOt%Qh%`TN zM%jl*39zav#y_%53JwS$1eN7?Iw$`kKRF~dcDUi}dakXA*Q)KYF(vKZ?Uie3m__!)pmlGy7H-*_u=UnL%0n+>dChs8 zJQtS*^S8A4x&q`HE;T>FcehI zjYRG#us8Inn48J@tk{r}UH$rXQ|o;;(W0W(3irI(8d!REze-PSczmr(7b~$XJ7Hl_qry(At}1T0I-y#B$Ys%T^s{xld7UQyo1%Q& zK4!0Ujpm?S8G1x=fFI4AXyT_|A$^sQ$ec?9ZrL3c6LkUOHd$Y@s?|9--EufC#-OnQbh%SleXD&~WR zp2G?WFl?w&XJvI+X1H)HWD?2J#tTp`E4$4h-^ssV5x}mjlv0s|> zTJJUfvw`v2m6eHg{}TX0Yz|&3eE*}GBW-B?jyBJQLVpz9jNjmzlhe$34v5B zipv$8DRu}BxZf9+iZOOCysPOuw3&djrG{+(F8q)Nh4v%&2QtaP#Mk`JuoGr~_#O_C zmdJO6>wpO)%KYeu96zru{vgT?=00>W++>EU=CteF#b`c4#)Q(m2%%Y7Bve+>KRu+> zyhBoNn#|>2&geD>GLNap6cRX0Dspv?dDmHU+7o4eH?Gw2-8^xvMAZyR0Uv*-L_ckUt{N7NZKfrX_vH+t>0-d}O1xFPoBrr*4E4?!&Vwpf!hc$$b~ag_ZGVj))mO3W)x$9; zpp*v}d*~kKiS+>i^du-AT37wnW^5U0aRwo!Qooq*W}h+bnMwC+V1DlsM3i599`LK+ z>74&&fdIHQWf=d>)A3M|`(_7TX|E|?TAn6Zd|D3mvm2a|siD1c<>^zj;IXe^@;fQO z=H2RBg2NT&67F}2dX5$CK0==EX>=L2SFiC-(U|qRh|)Ux2dy*d@D0nF7#v?eQoP+CE~$R-V|ivv?U&9i>vp{;oxD=c~4+CM{TR*~~_F;P!2q8LE-L)fq{Z8`* z!(mb?FSUA4k*j?_Zn*(t32;?QtIQ%#D;LoAaTtO*%Cy3rTo3-54@+I$ZA)cTpH)S8 zICp!US56LjKbmL-mAwgVuYvs}?|xMm)D#%I)!EShnyzhkZDj^aU1_B>0Pn&O#`E_C zxJIPS9fAS^P~FputZJD);r%&*&+OZ~c(bvRIt$Jt0G1$}DdXZQ_Z?sceq&i*Lk?3s zp8m+vH^S#V)cGDCbwBSelZOkkWZAvk$^!>R9V)n~zgmt*D_{EdxIJA(%jq(zO!#aSl$o2T! z`g@=fd1c5QIP=rwdr~_Bw5X8I=S|t_n^sTw=oHCWur%6t$^(e~eenx-_3wI7MmeR= zP{CY9z{;5b->$>O=Be&^*eD6WlwK@Lazh|>mUMzB9_#DS^New1qUOF78ZThUxAJiO zf>h&6v-F0o)O&sW;ws;g1mD82vnkbPBA#4I%f@|QnFVn3A^=5x-D4(s7BCR?bl)d* zJQMl7JKphfOjh(B_wFZ#qowAd!5&1`JKfE;4TxuP=homKkYVGAV31`J)xPpZ^?8Ip z5QcP?_jQY`(P0L{Onz@pNuz%3W1=-?kl*)}SXoz=I~XrZ=4y&HBJ?sEtx1v>2d^9(;Q0W3?t%td)*7uk5Qw&uFw(WM}L8o zC6gkhN!yqc5+F1qn2IhS0qB($X_^A>NSy0|uQrpdSntlQmH{rZ7jBLl^X1K~PA9XM za=Ha2tttT^h8_LR#%>Ogb0)?m0@O-*-}^RhB6c8zj8x#**b&&Q4Sc1%nEJNId=}2E zSFcu&kY920*IahCvat>;qKJhxhWOu0u3;ZS;zkeDA zOt%_hIswW|(%c&Dl(uya$MXFzp2tm{7%{cp8Za`z)f-&M}Vg|yK6)DB{gAVW~~;t{W$46l@K>@!~%Q<L0)*+DAh2YHJFAsOn{=vUnn50lrHO|;W%NF=4%9sZcIHN(YZIw_{^ z_Z&*~Ghb=(NTByLbQB^X|iYmL-_phD=5`4?boeD0nt1!`Ue0_RZ@Wfjw^KmWt8WVt6E?evC({$;@kC zLZN%jfrA+%DonEFLnSpEwff-;^dk(PUjL^~+z31EV#>(C&>O~_aa4?*`Pb3O;FT5M z`NvhCaXlBcrP3{#>OJIQ%tiQJx~ulMeG`4pt=Rrp)}s3|%JxH!-+%j!;eU@!ZOKr$ zbg9FBAZz=i?D8@6=a)AgX~UBfl1xolKm(1tm_lxOjDD}cwL_yKA3;1wHm~>2>O;~+ zq;|8MYc$zq{qKr35EdvIAU~xrwt7uoKnFV(wk2cW z2CP)GUr~zYMuUl3#@gA(3Ny>o#ZXNA%OP@&DfPb-o56|8@^RHd_cZ5OBTnErA!~CD z^uIrO2i0gG!v~m|-PKEV|HmgtI*%ND9{~kJ5#faOVfr7k9fBUflnoy4d*?9Ibz^<$ zjMm0=B^^v=$p69Ke?~>IE^WXtX2rDKfB^(l5Cue3a>f7%NE~uh$&N#ioKZv+1SE-M zB*#HA49P^!aU_Feh9Gf-A$-+?`|NXmeD7NCulLWxT00QB`|i4{uDa@~nr8I`>+{Qq8I z{=X9<*8D#kB3Af+(U|x@w?XXne{O?V;s4x*Y=!?nExiBdNGXevp%7R91g(VG7ASrN zQY+{Bh@RwDMMe#9vKR4WRqowo{<(Y4B09P_Ln`z0a&o3?K}=#j{7wU^g2mofev9qt zU5lGbxd=VZ7-F>T6bqgAXWF)kBlYFD)_4io&(AK~RWd@K>)yQ;(l76yhzgGM3=vJ? zt17a7kNu~j>@FEe&HtXO^7G4<2TgTnV<2Zp`L7E~P+={Wo^fMTXml2&3#nL0C32)@ zWw^)JG;8Q?48Bso6e?5H5UgvX)M6dswlE+|d897v1vh5pjm}epS3E_?+%WZGZ`PHfdSkX+=19lBu+)#y}QTsWo7BVyVf} zR`;GT-KmaCmzqV0@x;msuC2NQm2UGLEyAQoG#y>hB!hGRO(C(J5?}xGHj7@1f&Hkr z_sN-j>c#$)XvI40E7pBQ^a9w2%~YAdQJIu6b2YKyL|eEmvrEBMtzu$%AinJlwzd2>;J z)5T?}EP&3B{G9YwiNo?j2`X&mqo}asQ8&@@RY;wQFfJDHFm;tEpB7*L#@`qeD%x8=XcbTie78PB+IU_93&U{V|ci{UwJS?z(Im zn4EjFd!aXA)3o;XuU)ZQo2^pjbYfO33MQiM-$Kg8)m1?f>5ty=oWS#%_tG|%R$rfE zc|Ki%9m+9j>NJ~-ac(2&>yq$<3U}gIsr~$eon@r5MdaQnw_d4lw&W+|$615RK5nq< z9=K)s?G#?5JnmBq(5K7`;$iI|2#7g6)S~9pq7@LZQW%(62^c&Z9e&p00YQl4QnKp| zDlA$x$EiuZ=515y@{dvf9s%eSGCIz6sndGxu3viW>g_An{5qT@F4SQK4#PHZr5q)?uIZ`V~{gPR=-c-lXVy%9k)* z8mcQpjG&R2(UK$W+XKtqx=;>Ir$Y5?Vl$(=NyTG|Xgymw4AMy{wwSrgGp%d26>k!Y zvoz|lCa5HPsj%r5mQD3su3YbuqKmMLj?yaJ3_O)!?sJOIO$a*8sV9*We|~g{e{L^t zrJC4JPWK)~luDHHvA7O45aPnH#n;*N-Hf*1`yFT87|8uvlAde9-Vmc*G+T7ke6aAc zYiC9_HkRA?sh-PpJ-P#gi2Hne?BrR9&lj2DtZZyPGm$fN*oN3!Jn85sH2bGipo#JL z=NO3zI&1eAd46$k`cbMg&^>2KNYVIwaRIo+jOs5SWr zmrA&A$*x{oD|m}@Vp#T7_MVD@k$sZk6|!k6f(52uW;^{OPUas|lp7)AL+j%H6y4eh z{EU=Jrk+8)UL5KJvGXgUn51T9MV6)JZ2VkW>NHk0yTY-zvo~Tx8}`9M<` zR&e}lR_Eu<7a+C}C5a(iG|!lbUGWI2@>&m+Nm5PTm#91)boposrY}fzrp0}$K?Lc< zeXk=TRKAluBo!A8MaEgw+FoxI?3s?+D!v1td%L3>j}}$NWtw`W+mtoZruG%_y2Kee z_G~`F`@aR5ECF68V1-zKqW8^k{?S`ToR<75D%dxix}ZP~bpQGhG*@8qA%fV-&Z&UW z+w4cF&kZt|w8W6cVzIdSSJ9wPDUv_2Fm$!yX0*h1wwv~D=}y5rN6f(`t+1@}U zZEZdQ)%@XjI553^HRSzitR8s@ZgU0tCqRUP!W%xP311JzphUgR7CM-Nnf!b%Z{i;K z^vs?zCPXrXZf$Md8)*b2R1*pAydl(_WybjC6(gdYYpOA9%{>EyU>tH5in8M{b)=-F zRV;Yr3X5MU=1y-&`R6YYh>vsea{4z4JWFlOcV^YrTbV_CZD}b&U54x$aGl+S#2tUp zVV-EH{w)MV{{rYI7C)vq^wjuEKQg)Tqnusj;q*^^4wb<9lrrH0$F@8L-Qt46?ZTS| z%a7rYeqBc1B|+iQ4R<5W7K+6VYOy|BI+2F$lFs+i~tnMT}of}WX)?OZZ zO_Lu(T;nf&xE9=nbg~g>Ae&?uS&1$;9dix{OWb7wo>PW$mvhyStZVPs;;)A?>&`CSZKcVT8R%7=*CuZM+M>|`N_7Pt3=fd5069@YZXN`W1JuiDs9P*;c<^D$__O?p4KF{dK^CvqfgKe z0VV`zu_F8~HJHvF&$j4Owoh*jdHuTGNP@l40`vQGyHSIF<#^$OMbSKYaP^%E*dX&M z*jXfCcD)uedWDxxVt?KHBXa7hDwJ|PvW|{VmfNFkUG?>ry>Ag>$nhWzRBz(_TxRLG z1s!jv@aEL0&z>WfxyL;Km^%G&U&3t_*a7g#wB_B^7ajB0LuK&h16G5;m-_d6@BcgE z*{9fZh`SASBuxI=-C7+Uvb+C%*D`_vf&DN9FosGAG11*vPk2?ql6>N6jt}Lqv!A14 zTQVZ~*SBvB2qDh!ZvTspmsd4&Ewv2pe)E9=8TDiLUxO!jC6^f_HTbA?ZkG$%Bjy+q z+%_K}-*)R9Xc>kCg6%gpwd}NTV&|PI4|8+yud5%nUbcqVCn3Jq_a2AZ`Bftps)yjG zG5Ogr6gaLgW-kz4V`1^+>=LA0=$u5q5a;9h*J<+cCQ@yMO|e`LG~LnvV$~*RD6V00uxNIkM|s<`R|> ziimra+9h)$DHX=35-+rRsj6U4MLfG73I~YYnymc@N38E9>;0{m!}3)B`ckDU>AelE zMDups(ezXuD&)*^0%Py5Z+Cq~FnlTHfgfxV4Y^}AwX{FwNYPW10gmIx{m>Tl?nRHq z&jcTlvxdNwH?1F0gq&41uRFbm%ed*(t`q9U40BJzEe%Zc?xD%RcQ!gd`RF8m2DW1$ zrj`2&3ljHo68t(Z$v`Y_f06%keFG}8h>z!(BQhhVRVsfwE;s{5sv<4URf+(dA-AM{ z>IC1P{h~iC|K_y_U%B!g z-4z>?U3XBpy?<_j=7@ZcEJFE+D^mh40Q0HXG}XWO8_nt6?K9I;UA7k72OQE`L#{7t zUJ@o_BJS)LR*VcJ)lxF=gB$I2O0oRict0uqSdv z$aQwDkC$u?A(4JQ+!y#L2x@!5CCSNm*BCV~N2(1IpPpBZ>F!(Nt z$>}XsMEk%s-OUUy0fC#+f2=I|0(IhkU65CY)2h?L<_MEiN(aJ2=;J`ot#%i4cb1ob zzy^2|;nT_?rT{$BIE`cLUU$7diuy7u)-gHOB)I3JA#JkissT<>GMb4gU$C#E6; z*h9%L^0N!3uI*1b9|AiwLEmS00;!o`rswwUVO<%R>_g~Ia`K!}8ojM2Q>aV^rF3Ag z5??XGJ#^QJ=6uJ=9}2fDJ|Zt*iZHVPNaLok1!QRZTxyA=6IF?E!p?8)tutQ9%<{my z=2Gc4y~VaGD+dL)=l|LbN$=p{5PL%;eK!J>jG9M2^pSAzhSsE?Dh;PPl8l}}g;wtK z?*6XhpCS9;h!mqbxi%R7bSi4eABn~;>9M(BVWFRYu?~P+@nCd$UPo#SySSE zE^?z7?HT#4)x65IyjQ%)c3T!7mUmQF7MeL|@gqN{p4!)P7<#&Y0b;yC@J8*uNRhn6 zLj{Lk4Sn}%aHRND{-g`YfuAp4;U6E6PHMM}L?8M$7D>=|WgID7bbU_!H?|g@IKL!>`vwyG}(boU4D})}Zp8BDU&=T-9Gx+hR zKMTKlMBeNSxzs_x99qV7m-k~JRQ=pZYaG*%E>v=hZ|F-5&mp#1a5QX@Lfql>AEWyW zhuEPId~|kNIWG$3kxG_s%7tIAU@Y?=idoW1eWEcp+P0_@&vz}Bd)*CfN}f>mehJ5h zYUMw)1COS4CcKQ6JaYRUxElgrO8P^egbNbPKlyxq?iQEWj(87{f(ke2Kcqst2A--g zvOgui6!GM~$U;u;s!P&gFq)=aGenPIg#x(9`7{+R)!_@k3y|H?U2`#O0*W+4&h$1{ zzp&CjMnYwE^?1VqfLA1Tv{X))#-6>4cuBU%2BSW3To7KSN%$&Z*!#tA8&{S|F(zA)BaEQy0=yoDgOLgPMMh3LGlr(1{y3 zEH7Xv6GoGTFz5?V?!weZ!UY>TWKtGbtBn&L`vTc0w!QQj2)98B%0nq~L`O2A%cuLo z>aBu!X8DlwmuYO-+asw%B-AT^O!vZX^J9KGH5>NpQMu^@j=u>cPLP0~R|eGO@iru)?6 zi!fJV)2@07fhu~&F{`#{JB-&3;&c(p|hV<>JzY#Rj7b8XD5QpS|ZBjeM zUxv7t{hz2tx_o3&VVM3@)UzK(UpoSKwxj33kd${)c>m-zT6Ceiq$y>z)IZVOkD+Q8 zl|?S-VSmR`CPfh<)?It6kc6dMT=4OOcex(-;!DDDDh9Qdcot&!#J~Q@D`vfeZJQ9^6FVyoAe^N(XrlX+o)0oJ? z^LC*A%mL+ktc;{SmHmR1$T|h2R?ZU3OXqvcHtdn3hQ(oCHsocB%|Wn;)iTFu(d5|o z#9i0Yow7Ep`)Y=OL78L!Z(~9<*}D!Q2DvQU{8q~4pILx$OJtTE$%z3!T)*GFgPMvs z&8MorTy&i734_~;RTXk6iZxj{Pz@|8hUD*;Dd%OvTl0~umDk5R@bOZsM3VA!Z`h zy_)qM4!QmjEbW6N49Gc5!b#r}BO^z#o6bc?300c5`5HDMBm+Oi#-B^S5H~w;@RWc(S7xt&J3U2+#c$Vi$NsZf9tx zXiefHs|^aGyQXlZ3g(+X8I#jmE6W#FU3sJoE99_E!6K}!d(mDy&)@zsRRsB4i){0f z`=*ow>Il|rN>aLgU#U%1!MYJ&g?S$a5t zD?%hC9$xOu50D@7P9vtI$O6B@0Z=}X8W^swo;!-Ie6%8u;(T^vkT&8y)Ib@O%-`Po z$=y{`Ez{Xrd2wfV+j94(*kIs37OfR@3ktC2^&tP`#Lph!c~to_`Q1}QYY{5sbDIV= zCE>}O;4TVW0w>{9LZSu{9vrDysvuENe(n1^V(+O_2)7#pH-kB!`;ob1ndmiOxL*jz z&Q4D$vG^)sb76Gh1!etm^kZ&LWXbR?#buQW|nFO(njB4{W26i5l>K%T2`!` z_;}Af*?7jF%T5l1*5u(vYu>M=%6RwkW#7EWL5%(5l4BnPKXBRY+_tVd%w}?MV%0_1 zN!aorwq(Ltw`nzouqHXdod01vX5%>Cvc26ScH*te3afWke30aGW}jq=nRn51{I9EaO6$AX(sy!f;tIs z469Jz;>e*shLY|Y``PczzL4_1c9!e;#It&^>)pz2=UZRbEwUZltkT(i-~$B(r{oAV zy_TKY1`8gS(qUY2wY1zva`BR#bEq4a>spEX_K<;NA(iSEYPmf+^>x|J-`$mer(v8o zuG9Wm>Mvz#`L+>y{!fJ0b1%29cuDy@W1#11ek7uHC&6KlwnP&0-J%(&B175NN=rZ8 z7(JG?cUYP7C|9$K{f5i;TpILuTQW?*6oEXo{0I&5D?@jCHeBvf=jQM4(lUL8p3`TA zZos*!SsqwM30T|dui2->heprEsZ!|FJ>OUA|66ag^hy1S<_YX6vg1;C5I-XzbDrj&84qJr)-d?qcu0a-4t^nT=Ufoyh;GUb;b zPs+TjrApM=BtyKC;A=3w2tS*=NwZ3MRCFMNbQ`s}r;&-!)6hw`@IoG4FNvyrG9dzZ zl(-u8m7V6aaa3LN{eUvqhfJex;ma-h*TAr^%-|C?sTx$l*Kiw8ni`saZ13gR8<#`* zOzdjO23ss^OXaV2N9>;`mU$54EGe_!M~7BS_ZOL1^yJy^h#|K8GS#uhis?Hc_aJvl zi*YE&S*$Az@dT@Dtsa+L(Dh+2+q3mF@RJi3jE8(L38O zXjm1_+#!lM6L&@fi>)S0l;(TR%cme#rQ<6%5Yo2&NO~S{YpnxLg>rLSlg5Y4gSlzR zECC)^Fnl#?FAw#Yn_@1k#e!61LZy?SP@*~S_>?BpN$^q7^8L1y9e!QC^}HATCuK}& zC^#+(vAYIW?$|m{Ht9bw*xgzR`tx3>;m18aC-A)~hqm8`Z4X1T$MjwF?vUjPS(BPw8R zi-dTFz%Qk0H&~N4|9V&W@2^Z%G@!fhn#$$@wfysgwaD)CS=Wr3WL&p#x1V%&4`5h) z4cKsJPl4BLi%8-jsj9SDLiSLTfIHjk)dzd>lfHg^%cW}4Tz%7sbN5WGHu@V&qG|GNYoSON<_vPShi zwhfFOnbeAr%}$y3m>6B_b4(Vt-J`N_ts=0`R9SmJ2jBRg3r2iF+{vW8a?ah|{mV_m zj`#N4r$m09;?OQpt7(vLUfQC%cD;5h?&bSE!)71;W6}>` z(x$1;Qs+qaJL3aF6=ErF3nqq1HDJ@RbzfBtl?S6buh1dWh4ey|>)TNqb6b|`l&Iys zzYPDq58(>#J*}Gvl;3Or_R-UgW~nV^-;pqyL2&Ufey=2xfU>`6=f`tbd8IL!HCc-D%eH@3w zZV?Yp|C$@N6tw--TBB9`+x>isLSJRM<={;gc4s^(>LNvNh9~1(@87mNsc(RqRG6#> z9pDB<$CM^E%;h5*TT(4aCcjO-Us+r0-9A(kUteNE%~x2?eb#lab78@tH{7MWBsxrEvr0`gqw(u71CT_SL?)D=Y;I4%1cMLikBx_BENpS!(&r>;dqI29PXg4S(rte>J3-huHNOfJXMpivUeeK z!mCorV+UFT$0ki;-@f%ZC$tl~)AMVR!@|Mk=F)7@#?%a2Ue`cG?I%qhG>vN>PX{*A9wLxYSu>udrH~myNSM9#mNw zTQl$eNGT`RZb7NH+eX*gZ=rH-@_g_~J=ZmQa23JaRFc%(xmymcB6klS?+hBxPT8@Y zjAdBsbS)>IxtTriV|v46sQ*(np8D+PFZjd*4MoyuN(i;=^|rK{j&*6#|p#>Bfx38>OCauOArpn?JYK zI(BqOC~T;8`e4iTC>dqL#06Kkte9b8|L0A4#jNn*b;D_ow3Yz3#Uc;Ppf_g3NJ?8?_l&T)HoFiDWu& z7!nb7O$GSG=#90fYxYCBKlI;`ZXc1N@WA`NU7@$r{hXq%Yirx)&G^(gQAAg4GrMHw zi~OEww=cK%Q+&>g+L?D5=}BMk7i@`YihGC@C;8VXQ#`_+(P+}8f6ktfl?>kdd0b3C zyL5iW%e|q8MhXpo^XKcAmi>@OJZIKF-~Zo#|G%@q$!9cj!zCN5bZ*~P`DO->idL{_ z0dsB{W0I;1r*u`;_+a&_4A85VZVY%|EuAdQu6 z5xlSI=hou;nO^elq4@lbo>SL$Hzr(?sa8kv4B-C9drbPEFEmEP~=~| zfF#yS(&bKrw}I`oo><@bZQ5CT$YrkuZX1tSNPG}Z4*#a;JwutoFxHFCW;oAqnoXb4 zSP>o=SWf$lC>|~9x@P{ri}B@@8`j(#{uNdT>${)ZmyvIxk7YJd zFys~qqt|jl)YLW^8>`c*gatM>_R!o5s>+1Z@;ha&DYx>Tg$UQ&8KI`|PhIt3oW5$?SsAI@cZF5@^$FfS5o1inVriY1HC$ciEcTPfV@mgWJ9R%K+hRPgKqZC`nVF!$njlCTS& z#;>rqb>U1D2v0N9?kF%GpySZuFHlvfU%(L}jC_QlmSe8_F8B(vc6fF`i_rpy&h(%s zBnh8-87BQs{9-N@<*~!5p~r#kRIE=JmKTjXZQ%-48L{}%{vmhUvdqB+Y2^gs0gB;q zydm^BwOZg1NsBf=f_hy))AzY`){e=moR#8jN~dUVna1TDe9e6P+?q$?%&NGC#W4yG z|MBn~g{v4eF0)&?hmDO>^KD~oqgwq76$vScP64{Aq@_E(h4fm5kNp-c@&q(G+?Uaz zp{}}U=ep%Hbm%Btx0RK3ubE~ueJLq_*Z8e39&JAF8#Ji_v?3Myx`MQo1T2>uHa-1GHwJ$#B0?}Sgx~ya zJtl?wW?ei{^f%hGkjL8GCnxK>m4V^R)1WRbwuVf{>M1z3RS>8u&&|3^+9h(v)}NB) zV;=TP-S3OQnsMo8E-#!;zK)0b-;nnZJG{1l8E& zTnK@4<*Y?13(B*p-iAf$W>qt`L5bsO%YLj*^Nb8hs}@h+GaGz|DtXG`$>|%B*2YJ` zrqqIHe__X^wl=N2`IFVht1HT#yV1@NUtrL;N>`37T2;S2M>=udupFM+rS<09H1|By zOK-0y)9jTzk|u*FWdA@U!sc zb3y@E-m^{-=I=_!YvBjsf_Uf}bW{pnEX4X8C*t$OBE5f?>*f%#m5XoT!yTg%(n zD{tLbtCAEGj<-9#@v%d_sh|ncRl_sg(Qn`G ztTypoL5nQRP=~q~f9kdlBo2{@+e{mnsZUD-q6I&9!9-3FI!)DBVTobp1pEhWYw zQ>kMLJ`;|>pX6tS7?E#t4L~BNemsblqa+L|K+aIv5h4{%`3d7|1AM(!p&Aa{D9+L@ z(hAUZFBdyR1<=1XuC6bwyt)x5W>bsxMXSt&VkfT`^=~qo?_eEcfBuY5Qz8Dn4?E|s zr~IskXjwM<(zsd9tgu-8<>wZHJ$wh0%tWrVRG)R%!b60XiW)?AKV=u5eB zC8Me0I8T6yV5cZ%Io)zD&VLuwWUI!)g&2FKB z(7qK=o2wTN9^CvPv<%g>DU@4fQ6*BZVXF&rZv(ec%hXdR5J{`kMw{Dt(mR-bagJ+j z-67L?riw7A?#Co;aqFCz5TgL`^2f#Me7uKzWYH-C(qz)FE|^Hc~%z!_z%SN z#X8?#d=tDfg&Jjrj`fUJn$w=RH$XRNy;>WjV+`7W$2}6?`%d@LxcZ|Im4R-=E$9bAYKm`bfpG#|xP4I4R=oDC; z{~FR0@xd^#ODp}y6)GerW%aW_7Kh~53i+mA5Dl-7Uc2bHc1$j(QB}|FIda1e8mW8t z?RzWy7d?#C0x1W}YQn83QBLgZpXhxIz53 zVCtdOHS8J`Lzmqtu}sZy`bmG(16T}aV}$VoRdwWQ`BqzONT%%UVYVjDyqN9{-AORT=Usk>igErnNyLdiRL)Si>4|5 zVbO}S!AE*y%Nv-iPdo0L(wJRKXYq^qaLd&na$vrbQ92N93D3P_$CnNcSCzzljJ+L{ zZ$Wf!TG}{~f`Y3z6$Uhwi+GgnaX2YS{%E4OkZNpXaDy6Fb@TUR{-e>;MKuth5VZG$ z+@8$eHh|X>9w_;Y(yuJ>je>3mg2bYmyk?np;CmsE$F)O zHb>GKu&H~K+`SN|CO;c5?k?AXgHuhb83!hNBv^1<^x$Y z($I=$5dE5)6?H7YKk*`idX&pf@!49DU&6N+z~v|^tN|htiQkfs9am{+LS;1Hu>r+^ zEPMwD*W9gQM&Xs5LHRx`kX`AS*2$NI4<*e0bO}l~Pg}VoG5=JhJIlSr148z~;j{;Cl$B;Ns5Ecx}UPi7ew&U&hK(I}M~| zB?z8>s>b9um<8`20m;@ zK~~RIjxh8ib1L<)+}R!OM-2b7G;vm7U?7~c>+MAAxni=ECz^jvZDnb82GG+d{Hc4` z{ez*1%JvU48+~t2O)`AZIPvguOUww(0=z}y+103c3E{|voO; zi|NeR5!!QH@dNT(`zVk|Br6bO$8A(VrHkORf5iZ8ODRdwR5w>A$9_>goz6;&ABx$f z6C^MF%@7C%3h8^dRY)XcU*YDa*3E)fyJHIxq6<}%>DS*W(_;_}AsJ?Sxr-nT0jYOd zPdf8kxot1Q?=DWr{&i|f(`}@1sAG^GhV%W`+xceE>Vx0QH1KiP5irhBPo5!04tD2< zb3*InXW~IbZ}2R;J%~sS!f#n`AQXv>oz%>(mqTASp~lv|%rWFqIdIo87x3t*7oN4-f|@+k%IGg(Rl4wUqBE1Y}9Nc3*WS-Vv_v zX+3gjYh$H7mNzSy`Gqj34~WnM;>0^X4<~j=!_cHf+?1{xo0#ez2xW8rqd6G~i4$Ev zzDwsauzysdpg0bhziDRpW58u;CIiXVvmD=TJD{P*RauO*@DP9LVn>}@wK@l@)!L}H zFq&t`%y|=B9Dd}#_r&1?<;haJdFb|#n^HMfFN*q4|K|LHuaQCp$8=43OI-!CL!Dqo zO(9;c#4nMzAv<9%vfMekzv#5h%EHdOw7j}eCu+ecd8DP4wJ_h_jL{Z;wslhB7(9l1 z|Nq!E;M=qcq}4`|JHn6Mq0w{+3PPx3RKF!K35c)0`-?FJYDFj??<`VNC|W=RIrI4~ z$oX3fv$6*Hah3EN8cKn8Mdp7fM6SmJp_l{8$A)8C&A|8k zzKjrXp0|y&ESQ>tetYaSRO`KE7~vAr-t+58pHhGD&vRV9C@K%G2h1R;F8nw}>*9L*e$!_0a}1|@ z<2!y(pQa(S^x5-HS-33|OoSR=?*}~us;RBc{%=fcHLyChN9ZNF2k_GqnyjV05aG+V<;pvmOf*vlv2t1z9Z>tr&J1-2$L+uBwBxa-+WX9tDNY+pnw+1J$Qgqv(}*pXLs@w{jHdm6$c*y2y-}bWmQ1Gm^PAMdSd=d?;ED&J*3msG zWx?mj2j@Rt736pjJ`l$V31$xvN3$hoTMW~UxbYSyb7K(qkmv^yQJmCBcf9GOavzO+ zfQ!>g5J`J^5xkOGEZGR}q`NeO4q^rt2lxBo;7elIkS(6o(O=^B?V>_y(vLaSTrJtI zB83)S$E}GdOv0yBjlAG9^=jtU&cd9PpXVH|wB&FXQ)$3$Bk*_8^I)NjHvU>ncO%?s z=RJt$(^r?5j;;Xwb8HCS^**Ky5Yx{54$Q{Z=M%se3Z7iK4%M3n z`y)RTw{~1e=g;TuLqiHc9*Vc{B4c)=>%OrB+Rm!tx(Vo0LbVBf!E}7@QK&;QYh?Pn zH6RJUE#U3F1IzrnK#A!v7>MG#835sG{#ZU*_;y3swRcb!-TYcbefENNz+V`R(sm7X zLr`^|@kfUVvtUPOEd~8zISb@p!EHm?m8+_P6Pef`MUXU?b8v-d&~3N-@0#~L@qq`d zny%KXKxA~i^3(qPZqL8qcd~`f*68JDMjyTB&-d7h>Mv`dG*qq;a{ewNd>hAx!fDMN zM}DHEUH2b6c-Bjx<0Q#+{#1NO2^T06`=z^3-8h|@4Hb2B0m!lth-}x`u*<#UI0jsl zsB3lcAWpAl)gFspOu`naJ5_vK61}ddVo1m)nu9*o+j!=3TAc(6@{(6t!)0kmY!Dqu z0K#uE8@ovsY@i4_a8M$MS6HsFZecebt~Pk}B3?ju*@94Cf?XbdHkpgoZnAZOdNC=w zuh5iGS{fqkZsoZhyNANYaDIWw*9I3 zpvvMkck7!vRSfF9)q}NhhD|G6Iiu4?wP^SS>E0reNRgr_qWW?3fL!yQQ!`OrUpoUp zC8@mN%kRGly{`y478L?!!iA8+fx$FPU1Z*n`!y>%N*kB|@5$>x%B^Cm*zL8A!eW>uqoOd#jCwOZq58CAOUmP`u-!lQ-*4INH84w7KC^Nmy7gS@F2G^|PUO z4mHY>+;je#SS1c`WX&IK&UX`DhjA6kMIdkpZm)YErCVW`Xv&W1=7gWhL3AcJvp^|k z>SJV{mtRUSo`6z|>+k*lfHgF2;`WV0dX|An6GZfZqq6kE({~kTWoVyoZ8wSGwkjZU zVY;e%)d-lW4?R5-SR96`f)<^#c*>vFe+7}j_c6BMKA?i)=1Kf#z|hMrX)%8fK0Wb1 zt3Mey1xLx! zZ#BzTScds(ew0|FB$W z?DaYhIyg>KN_Tan;;^m=6%G#YS6g{46#{c}RMlb_mu(B#?3kXyGM$QeMlWW6ZbtkKubB~OBM8xEcv66ER5)~b!TcOD&DY4(}aq_8K88UCpUp!)H!i{XO;vK2V|%4U9@c>oDAL}!oTY;Xgj=;6I0NI!T|eK&?2 zP4aYuAzoS`)4hE$mejrBG6BpPtH2}VEh0AH`b0yYk+Dknm#p*?o?J-XofOI@_t)3D5C6@YC=WGHQkGO z0>u*eQo)@8$Mp@qxrW>Dhn^M8t8=;}-@C4r4?4NW=ARcZ5TNWI4rE?!1A9ORD-Mdj zf~{z=)_s5fH$1@`4*+@*;RAMkYGs-$C=ui~wD5w{gN>ZOy0g%VS#z6{9;M*5v|#!#XosPMK;0{XPOPp96hlu?MY0- zn&mkY%kG+`mlpSR3X6e$4KPa0GpiRc`{@XUDLoWP*u6$~1z;#tVaRADs4U2}z5a$Q&UrsYTBN;KGYq{r>o0GL9u)}>8 z9hd=Jnni3(SouNSODs)Pdva;v0&}G!K(pp)C99t>E`rcj&Jjq;-;BI{El+C4c95pM`?+ws&Db08&!! zY%H;hdi)PWS`GfrmhN~;NN*h+(l5L(B{E}Mi~`4Dqb3@PV+AD9!2Ok z%cNI0@>t9tvpqiZcs2u_FC|xGOJI@SC`(N2hF*+9p%BcJ*fB!(0 zfp6_2HhMYqI)u35ZeR55nM2aHZdSiR;>ypto zD6)~^cR@v5a!Dj6Tp3Lnj_dw)m=ag8A$y;m?^>mxUN_6aNNl$OWW$2GUdrcty0SGj zRQfOVxf{p&0j(qz1bS zeJ~LiCM8|b*(>=elND9L4gahIs^c)zfP8gi=c{vNQDW;uMvqIopn^)LXIOeQl{K9 zNAEoJuzHJ>iSS{9RM6}a&cp*LgLBF_@YWSCmfbM23zkeQZpM?f0P<`!(p>MUFQ`6D zrSKlam}h;g$LVjqK6&B4EoK+Ncko1U!|xL45KF54fcv_ZH*xM?2D13ek+av5pr3bOHpKs}Np{I!fR`HWqb zv>AI5k5eg65obZ;(w?x~8%Bj7-X{QS&>C|nrfh&gK-%>m9p>k1&9j4$LgqoN!B}uw z@D*;ePFu*>xN#k*@c=-PIdl-a7B@KZHwuWH=W)Yr}j(a@$01q9vv z<`tAKMfU+FFD5oromLA95bYRf5mE+L`8(a;6^|sKiNwO9`4$5>byh!F3#GQkz%*6p z1)ID=lCwYq3yQ!ry^Hw!=ctW#kvZtzzI$xIQD!&m1t)&vF`@woGAg>~9c2c5g zG+ekw8BH&!fJ`CzbPq_`O;>Na_1PmhA zoNp5Ig|Fg-ebEbtx^_TmubFruBp+}(BJvSNOr8U#ddIRc& zP`AY-De84jTmPo0$oQ$$*Sz}hrnA;U5X0MgFDZ6cwY0Pp*E!roBmpR#p#6%5n%59S z)Q<`x*wjbFdg`-rP~vDp7D$cHZRg0xZ6`x;)3}f@l$Go%Fst?v4!?5w zC?cH#Qvio%$}lV|+O}WI>T0&t-KU#9+FR-AElO@z!WfsW-x9zC$O0S7YthK*6Siod zv{dx-hGRw$%^!4i9+-pTP!{GP9&TH+-;RR|r-eR;feh2yxaWu>7)_sywSn5#4gk=n z*u*kgz#h>%q@x7~EPEcH@OFgYhKQi&5SN!xacN3kt`y=m$mpfO!9k%*^Xiqaf9S+A z=)^p%UlD-Qy#xJfjHkx5_7l8s*~|iC8aik~vOa0ngyTb69GLCFf2$^~G2`Sb{2<4G z7<@lyZO*O5V6B0!mtpF4>}67(y`y3Cr9VbqI*1tDrmSpadmI9r!Ht`s>H zFYU)h>hctP_sgy{oL3twCpvt0p2w$z!K7f^P6W4HXy_0dY^L9d;24{0+Gs{PXE#eK zs{hWX%ge7)bnGESd#iXpCGo68XlQM4N@Mt%a-d{Df*8CdLU<`(mP_o)X{#T$knu6(pv+m32;OB00J{^)M}M-g{3CuLgPD%|I5^=m z9S!o*eY7CI_HH*bGD{z6VSzqP-E*_s*myJOU?bgC^Rreyl69owxUPLhXnCUvN(B&q z`bH6Xq#(^1X!JYg)fq=`DSFbx)Pd-?Emg4|S!^>u^K4_YFSbj?^e6J;wk$b*6S-xKIM$`Af1X?HbSe&f4(hn+_ z5ssWo#YUk)yy8+2nLmYk++lT$!myM1q*#01zz)TSIaZnQD0p%p~l5u#2 zs%zoG=i4B5I~fKPO+?*E&RT(9noto&NmVs;@k5Hs7aP|jdQd(xnWz6NJX1J+vu6HY zGmVt1m0O#e!p7ybYe-&!QGq=pTOyC$BN7~Bn#;N2(}AFdrt5JFDz0s=w+OC~R)|Ct zKJI$8dlvotp9)`CaDNez)ewwrUh{(rae&GAi6$A@K1VxmnA~{PaGMGRKq6J6}0s?}7NX{yfGXh19l9R+D*P9FU-g|!i z`t|GnUcZ03`98~3wQKFQW*BqKvDTjNxYU?oxM?VH3L2tvnK4Or2tzJb)Y=9;?hqYm zeICt;iIRj^`1PRcqwj+|b>#+On-c7j0%u+v-LNLJ2Y2e;W3Tv4;o6qEcT1>z+&s!g zcM7^G74MjWAEP=F`bVQ*;T(_CT*LPSFUsm=LE7@qGhn)W6Z1tEQ1#P|sfKbUS`IOM zQSJW!qkm4ifo^A0uU|Vu7f*^WL@Th*MyTI_tsdxW*#+})L1!77 zK`#;$xv&H7SNC^B6kWv&G(3%enXCLd5OwFCj2$hCg|pZ1WRcSg^Z$#lhw(lZ%DJBB zX*5*|8;8iqx)V_=?-hNCT6iGr-B|=7P?eNM0!-j9k_aa1B66{`&Lo3p$BYOd+cUUd zHEDa!R7(@k0)iRPmo@;@1XIt@MF&!yMc771wr7t)Nk_nLyML^8K;deK8bFYY{DXH8 zg)<)TL5)vw}TM2kZuI%upvc;qho7;S3dkkQ+R8>VyYn)V341AM3 z2z3Cp-?sRrSP(tv53N#AN@i>lL&5EiL(2)oG5B1$zu3hvQ6L77{tDb)6tH&yvEM_77lGRXx`0 zPJ!4j9#pBe3G@2UTf!?QNTWGP0lNj~1XF5d%=jKx0zr;JsbBPmDp8U#KVW%nK-V zsI!}e*-6QtKd1W8yE0C-?Ex21H`}C46)SSf7TZqI2ChpyzBPCMs1de(+|KWd8wNy# z9td8gM&Rs9uhamx_df8GOTaecK=X$m6yr>f#AyHeHBhi4gGv@kV5mNoz)-2g#!t}g zS|s(&&MJXTk3$Yb9)LFPk-^GC!b7hiV@{`b0jNB$`eXOMWHOin#{X&6%>QPQ&i|6H zi-1iP5|c~Fk6&Dml$6%F0K%SIFi8TtAyn-Ek;C>Y#tpWkkU@zYkeYfEsCpo1s9Ir{ zozZN3UfXm#9< zmg3L7Zr?Yi;P2-@4)VO_G`WFfy3N1|G=co{A9uzLDz4+$tq=g_hQJ|LZR~Nr^<% zoikM+c;pi`=7_LA1<3_r2hID&-*0^_yeZ{KWIn1JP9l+J+xx>PC-wMDQu+uKkFIAX zMD1DC)1lC;koQuFIrBb~iWr;Ou_|byK+aivW==;wF5;*GHiP*}ndem2Ij@HqDVI(r z6m&|&G~}oDWts13vOOOuY*k034B;-LJ#=)ayvuyV`qeptO|4C>@u^`;&3eM311RUB z>TjGcb@MCak$crDL;N%3}!sNU9kW)Vfpr_56n` z*X3`ujvpAzJbOD?Y1YxT?wp3Rb8aM^u!I$6&aTUw7BR>r+If#pl)SAU5 zLrq>Hp{&^Ix2K!t-uoIl%-uRVZKqisc;+o@z}t;V9eUia)Wnp=)-DEJ{ghj_!}3EP zwOO#Tl(hrhD{ALmBgkjNl#@E%lx)vV`%V;7_Y|VFM!+x+*7T1!)oB2kvda+%?TQ66{J0&1v zLP2T!6|;-RD(t}pa!UGlL)FKO2Zv1^M9-!Fz6&sP=ap2_ZL4`H zHmr7-h|WhsM{8VE)P?#ZzRQ(bYc^g?)I(`C&P|Oruims~31O8je(^=JUl6`5TV-b6 zyfUvn|86Hlh3$=U=oNF|9@TaJd$sbx?}TlO&g2&=xpGP+j8)#u*Ceg5cJK1%_Ua4WGkdW3>%vYc-YUi^t7-6Xb%(|2Nv_kAd!3^G%N2Kt z}rzBrTdvG9IWSq zm;(P?24xHk-wS7tQ(rC+LyuNJJSbRgl+>U;GYt>P!$tS}wZcDGW*1@2AnMeCY}k+X zl=W|#Wekk?g`wij{CeN1cYF1)c!o0vhp4CL&cFN=X>?<}>$!Y{W$|P@-7?tSpN)}Q zk!25``*>*-pM-s4)#kFkiVDH*Oz3&ZymJ+u^fogal+}-JK1%a1+^JYG*8l0hc>@{wYdrL(#<{7hM96!1OTCD9? zH|=N((Ps(_%xC1hq?c||2<&mN{=9h(t8CZy*MCj)%aG`$@4!4OkHn5vdS4S!>ohpD z{L{enW)J;k`Rnqr|Ml9vaP9ABoSojB7QSpy+YzYZxE?F2!9z#32SmdgCpY$Co6?hR zHyr;jD8QVoVYl@9E}`Iz{`Vt`-&QHqJ_|_Wsi?&+-u!#OX5-U+f!EBd<6R;x;R&jX zjcOul3}^-4`|>KgRmu#aNkp+>`xQf~4HtOq8m94ZJGO!!tAOJrP0L7743-IGr#1Ks+1-}-7hb<8!fxkXj68vlw0qwT;FZC@z{SsTyy`Scy75T)B=d310sg1` zGmgf_p%7iK;PHNcFHR;N1wUH;wGDE&8fPc~x}EU;_JKL<)0?~O3Gkpp0FQ7m-487@3$DqIpcFO8Qy7XH)emG2VH8j$Zt!QjIbNcx1d4m7moKg z_F$VrpH9Z^#JFu9yLXmT4+J-r8k}omvO`5hPi0Sl<^0Jo7*jtm8feyZ<}C0RnahvPN3tFX=YeTfq+Y z`T<2tEj^xGl1&`~V6<>={B$>y)K~~}XFp3M zS87y+9fRc{ha}&>f}h{Ut*7L;LYAcDZ^pr;f4}>HTbG&t!F0>5(ptf{ddx(%EK!iD z9KbAJ5F7!eJ!+AgF)%g)@r?xwLFm>(8;7QpNe~w$<6Bs{#Bn-ELk7lXuK?-f#+4J< zjdNq`ygr0#-PIS@zN=N{0sCCHVda6$az&%crUQdGXQ!GwK6E0=?Bh~?Q(rE&Px*ZA zq;F}py)aW#QjweWWUT_onp04CB-PzCyr zd&J0c*(^B|*3S<*MtVRJu9ey8I!1DG@`c=<^^CnXAD5m@#?z~; z5K5TXkAVl0d-dEN9MP^YN_s8Q{ITsNzx<2HNlW~I7exLm`7S|S#4*A<-E|*VbeD-ptO`w$McL>`EcjR zG?UqAIaV93iqkGLlpQ(tp=k`H#FKi=R8V#OI#IWGWP2alHg1w>+)VBIuwq(kUMz zTG|$Ywms#%Es{QRFTtu;)3Sac)+=RV;?hV`s%c<%4~JF4-MdNWGKk^5)p@SI@CDfM z0*mFqxu;s42(7&vp2!pwmRnnzY%U_&a*A*&;cv~(vg1WP8b6%oJr_|AhFvxH*=%IP zLbTJzD+r>%tpYJ^$KI7fD9>oD;{H@GZtq5JzDAacFjZ7oT z+UZNWG4rZWL3U*8$7c->b2K{@LiM-u1&+8VhT{lN)Q*`5JMiq;vqx)g39975*atW3 z)D#pW5;1;Q00pQ5vyA>UpNw-dD;^72lvR|sy7FmSK4RGNf#ru+Oi2@S9TQYcLYH=& z@`FlO1-{9t?~mOdP}(cGdIxU}m?bcCgFCh86t+w=8m?MqzCM#>@|qP4;!#GHz3JL$ z38lkBDq`3eEDj_Pt=pTHxU$vmw-iuU<74aYyJwGVGE1635-^7<^NO+Qn$RRJJUuD9 zqqEb;)%O79yqZgO?J?|G_xy<}2BW2p5rsFxbSMlE*qG~kUpaAM%NSXTwEk7F9gvtl zZP9Jhn;$)e>OHM$cgo_g%eZP`#&3C#>cz&7QBfHj=v2oQYKaWnqsH=YvVY>+Uo3q@qu9dXwLSn6&eSOU5-SobNAv^CSOYb%)_kKIIe0 z9ifwg2asRwX&qb7x-pi-0wMh&757v;UBTP6aR%EohNjSUmX(OI7}92J>=QfcONZFlr|K`uSb~lQY;8!)S3jyCNBLvG33+?`(KID|><oOO#+V;4A??hh(^z@!*R~2h)!%0u!`VfaWPOSK)>*{y zgFr=Yqa(iaQ12ME<1gc5Cv8Nkc)NydMm_*Oq)gt6hK6kdIzz`r8TYmgQV5e(gl&#S z;`+pWY`9g5w%1{vyy?Nhrn7_2);SuGG9oF#EEhsCA(+iRLb1a&280 z0RN0fv;E#mm@+ILJ_!?AcKI4GEv5PMN2#dnYI~|f?bV_F`D(`ui4!tNl<<7kXk5h= zEJ085Z?q(^+=Sb1m9axtnGstiE5{C01NshfA4nY#Wep%DFm>*MHS2ZE_kP0z7U+*? z!DAjZ@AQChiUH)2+U|&j)b%D5mHS7jI3ZF#YE5md$)EXI0!U@UFWX?}@{h{WZ!7Z| z7MtRT5(y-&nhOe(Zo*uXQwTbCEn2>V41eqXnU+B9&8rCA9$VT)QQW_#MJ@5ZPDBnW zbZ)+ewSnPZ{lW=$xFW)Q@n(yTTEXby=er%5o;r4?NVmaMWsU{dK+U=^6i8}t5RN-4^0SuQBhya zX8y7)c27M*&Dl0$Y1`8OXzbS;MH{GpLhy%X(z+TuTBmIPAn|*n`^d@t0R;f!=jD!H zI2CAJu9`K<6SJ(Ajk4I&_UGlU3vK+^-nv4F4Hn6Kw|Mzp><+rNT3<|(Zl8FBA_CxQ zHZ0JvN$inoY}hhB#5<#cpYOG9YIP$JP|eLwxA8x%v$z4B11N>kg8SrvhX5EsP8{%| zG!MJ1{J~^7`9(@~0LOe>F{Es|^1E!^BS2)Bx%yU*!)E$Y>QHg#=XY?lD1@1mW90E) zj-w?-zpWm;k*4 zlmHYdXvcldPWA~YKLie<_|bR$;Vic=3rQ}aS%lN!EXz$qkVAW?y6->ejDLQ>8^)2n zFnjrP7u7pKsCK4i{lG?1tHq%b$VP;$@C#7M{+F8Y+5Z)rv1@Z#Duby{dbisNgX1J; z(*Fsw@qMoxxH%gCR$ADaG?LdTn)9c6`E`1_ai@*}eOzF)W&qYxRm^m|`=tXN1IEov z#@DB*;ujxKJjr0uS2vRt!t-xLGPFq@N9Paxt9UKDS5NdO~U6r$6#kSY4KzgX48s&hG2_`G5s~07v};SRxIX zg&YoKb0^%iqAkWf;2H}LW#b^OmX=<>apel1Pm?cjk62}m$=zoh*8@L$zlv4DV4f<2 zO*x#JD7_fK5C07W=3M`dFNehI=?=K|Ci%9~6^D-b#v#f=vVaLvXO*+F2Vhwg1Z*E1 z=#|4@TzefGIy&MYgWzMKxgn8nL?-+TLw&KoxxSf6>H|uckj1Q+gQQbicN`b9dJkB0 z94OOqpz{iYvoGs;wu7$^OHb^8ctdMVTLPpF;#w|XXddkO(TgLl^x@yra`lAQGoI-F zU3ov0>Q4ApaOP>^4}P$x3i^!=dIz8ohYFQqk19&VQ}9YC&Qgu;unJp!6E~cWxI9!$ zK(z6U+jRwE?iuaXARq`wsmBzd9*jow;12VSsCQ$tgS5NADaDzuX}^xsmQ1^JU0=*+ zpk)699LUnc@DM~Nm{oPidysT80Lwu68?d%|kdAD0$Mc&~%D4ipV^^O6Du2(vCjrL6 zixs^~qJ{ON5#W;Cl&QqNJiz-G=F8Eb$fc-DwS)->K6E8}A#=hk^ItjwXVX+dI#CcY zR?$$Fmpc=+m+Jb)m7#Yf^1=3$6#Q;vLDxd&(`i2kFeB#kY1w06$=guXZN(@Uaj?BF zV!`PKcLE!H%zR=1BGuNp0Bl&DXtVcQ!3pvvz%ZMIJ8(+3^;00b(mJ>VES&@>_`ht7 zz75qCLr3TForm5W1babFvy(9RFGuBxMJBT(7j!bL~8WAZNk z`*4jflp0bdG(MOGxBom3rbcopu?c@rmyNDiQY5Pt+!YtgBh%nvJ*g5A($ z87(eo?0>X)wPzXVA;fd9UdAXuh{tFI^A@iMDv$6?+S-FJC}3Zxe)DDL7B`WJoJ@zT ziSV2+iy1>ps_8wcmg7JhLuFP)4OzgYf*#fnnD3T&`$68Jy?bxK&lKG{c+sVfp(!}#$x{!`)Tk?n;%Pi1?AUFOKKM;@W)wP1zyj)26yD`L zM|JNkc6>4{Q-4NMg*iC@I-$-{z*E3_4k;x_DBIi$@Hm*XYLJXN2lAH+@NwNIpzJP} z^y*!E3J7Il^DfcBM$$_EYf7r~y8+_nJ?kre4{I(D*F6T}Yg5^!AxCEjzVFm4=2X<$ zIi;9aYo`rCyXu*T575j2jendGRxfbr?Y=vpJXcni-J=AK-91bJ-VgkQ!2GlrxZ8tl z`#~s3YK(&&wF_(MTjY!#!-es23jNc!U?#C3Vv?Jhnt~bdF8AFWIFahs*#e}d!$;Rl z^7x`>u+V?4Y3dfY(Z-#5`+m4rzJ!ZvxiLnUL z&Z*Wp$d4_1?Pxyfq;3wpk_hXa+FH^=g}SX{3``$39G3&d)avEcBV~-VFjR4O@*<1T zqHU7L|BzJs#J6a%{c5I1yR|AlLJ4b`{b8-`^EuT-XheUElzPD61)S=!a#b!KP8Cb6 zVPwxf00Bj{^oXf|uUMMw9)VMi03U-PIS>0h4e)@-s+&6J4%MBbzN?snd+Fcze^Ou} z3CuLU>JW07%QeCw@ynY4bsan2j|`Pf2xjSXDgVM}4>$Cow1&oN(l@n4s2z-ZBf{b4c%fbd zGXXYwmo_Tn+M2~&PGXr#>pKS7eWJ&n+m~oATWb?@`OK+i>WCHe#oeFo;{CUXr?aTR zj|i(pspW>&5&IH})Fj8q=>p4zJK?HJ?NGLm&u3YiLB@;TVx)SM4G-jv=oPJY}F7-#J zlhyk~$*F>KthNul%U>6uMl7d(GSq7H^Yvh+Me{N0W=js!0jk;)Lgpq8x`nLZ;G@wZ z?@ET0A>qB}y|OlZP?^@gWaB*iyJt$jhNskBC}fV5`@+P)xS+_51~miudM_!I7fOoJ zqweFEQ=dKC&Fy?K1WM6|QZ0R!U+ZgcZ@yt2``Ub$sU*_|H4A}fZKRPbJp)P-m$h%{ zi0ZcM88abF*PVxOI$)=`Sxf4b?`VM$Bav+2U%9KLZzA)$%7tOK4z0ntm z`sT~!UkSDkB%Fny1qC)(DRu2WU|u4C}Ed&;mN_v8mPaoE*1~2A_^?2|A7)74dRYl3n4p3B| z90K2CtBg6)nTdy%(fr7U#(P~?WlQV_2v{tWd8J!0n z*bhE{p7|LklMj}Alk%_F1D6cV?_8H$rq2-8Py;V(?kNi9pm74wg4nmz>(GzFNW8P~ zyox_N&xGKLhp$s&=O8<6eP@-JN1!90&D;7ZI(kxdj^8&;0UIw`VOd07!Rn!wk8FRl z#DeK+j3E<_<-0e%PFHS2Amjx?ZZVmmmh@Z?lT;qU z<>rNz1*#6#gOc!^>00>En=~F3h+r`WbkM-3*K+6?owOCPN{cjGE>P^r2EGKK(=}+U z#X)hfI>jBT0zw67u1YL?`DlFB8^GhHzwj$2u~S%2rcROZ+l1Z@=*Bu-zuc@2mHj!- zxal!`7t~h{U0D-1ip;db#R%*(o!yUJR+`EotVGqOlIl1j9*sbUV>EfDVRsG{_Q&*V z)U3Xkpj!T9I)E#J z>)TM#8FCTVO=I6FSY^AyVRDeYuOK&%SjeS6L6H(`q1@-*gL_0 zsRCk%gxMO<(pvSJW3$VDL$?4;gk3$elffXE`32@Ni(K!5`v(P$hr4kO$u}3r0w|Iv zdU_tbYyg3T-Dxq0k98wLK#Q`j@O-qL3hGe>k|`6^5k7|gw#Dk-0wIMjfIMB}Z-_Vi z=Fc2myi0mDZd?ub8?dxY;l|BeBp%|Y&f}bu^(~*80s@NPX_-Q6qW4ky`}*F{wQ28y zCnvX5G?9+EYlS%;g=1!Rq0>Fw@Z=)2o-n-`00`c&H&NTYO6V_2Fchu_P`kNE#j39YUIZZGQS`x&tGx8CqumEws85d92Sq6R!KP;xfQ>(p@ z^LJeBLf#d+duM5Bt0cVnfT{Uhpada*Ychp_TmAWFFX}Ym4>~QRm50SE>+hX=W_Zpq zKsayH7FT!>h_F_gyV7%E1PR??34Cmov`67mXQzlVJUPG-A3GkCS;j9KK84!T0eq%$ zs_q~#pfW1b!c*ZocNamNj#8ybqz;^fY=gRSKze@!7K;`a8CO$~)M=W+EHwFd`sE7)w)w@2x^#9<{R=xmDkmT{b_GD=BsHMbuL%j+V9%(*n2iqe6(DJozOu5&> zz&=7%;4n@oZh|5mky^;7UrOa_YLnJ_6IU~2;kuBGPz);)0E@BgxdYuZg85|E-r)r3 zE<%kNAU@~<`TQn4_RqWE1QyZ*Z-_9oQLm0O2%avs2y}s7(W+mj23!dRI-5E>kdPTc z86XO70e$_-peE=y-vDt3^8GXgbI*9-5%sRc_(NUpm%wjzty5F^oYxW^cx>nXr-f6q8g-B2L2ngH`XqI^NMga_z41B(O?^@X4#wF;4LILmri3&0SfvP(Y*&=K8yn=v=V1~p- zJ{R{n=k^*CUYRM6Xgts)*eGVdhEn={U6CRX6+S1P#JE`RxB3QEvxmLT@-KRa7TPVE zC-tKr6x*c5Qd0cX(*I{f^r)6c|Jday6=d>&l_BXXROT7+JhPU1xHxu9( z^mf{svB(sZaf=|;YD;3 zVIN~YCu`n1!D+rAu-gobzy90MbJ|kE+PIlI5`g852Ya2mV6Jf|A;bHXz;FQQr6*M= zi$3b#M&u$GGHN>j{rP&sItI81sAEHIDY~XF#X-jlbhP5bZJ^$nw52JiJ_`8}3O(<4 zMyN{#6&18Z%UrUDhiTZ4O}{hjva+@g-&@0AG`+|Gt>522qprR0_F3dIIWJ$tHf^L& zPqnN<;eqrLATVRIIi|z7O}+w#Gj-4nFJA@B3MzzNnj!+|OQ9r2vSrY0q*OVgIuw)w zG;0|hT+IW^@(|Pg(_MU26|0bXbmepD=Q}68b*@1ifgU~zOFyrI8fp87?}|k^rvuUh zKp83-m^%`e<8KO6jLaO0TELOK4)I37dd5U`^gpt%Az1cHc3D|zRuIw_ti3_YOS3jv z%b(ZT|IG%9w`FnYp#1(hAl_x^>GC7A-#H(|LX0oU?LN#v`|9`4uD$=cCFehtgKo^r z&{3e+}!X$6g_aK6=9LJ4V|TqhA%GuDgl2LKvXOhgki{ZjS+=w=-GZLuR(FerVwM{ z@DB3AkbxzL_8*sl2a)H9NqC?(j++@M3|XLbMX`A0dA@=74&aTI?>BbhV_&>rZaNMO zHjD1CCeShqAW%a?4CI8=1xD9xLaU&oaU5y{Sko;=KG_sK?JMwWV@F(pFfz6)Iv9y4 zi*Y$vvyPC^7yw2`q0xU`XiuAOt7m-V&C0bk6%enWKEO`kkr569?F*_FfHXggzUvDV zsAXMKNS=c9CeR;{oxMLc==Cs{6V8_{`Uw0%ur?#c?J6*uQQEOmC$J8-E#!2&M=xR& z`m~`IQbKB#te6XkeSpLe`{Aa5(D-e-c;+zZRm=G@`fVl2OWWj7koykIfhzAbNDK&c zL0)|WpAnG?eW%1CcW_yUgamnqFz!*0H;EfE8~R2D>l#onhEXnE0^S@*@l@(o^9H~R z_}Jgo*MtYf;M!l9zL_;L&>UX}IhP7BpFpO7?AHM9Bgb%W&YkJlee3nWomxAtvx1Ir z)nmioWN0s-5`t*4qT0iy5 zSMS#TsO(_BQ@^w!mq5o}7F?(SIxtc7&_?!or3UVyDvjLj2OJ85X;A(Dm}YnDo}ow1 zC~+GlwJinf3&^psPvfHWcJCCk3!Z}s@6$+wN;)@n<_17chz6zCy=ke+O@ZDJ;e3M( zDCp}iVqs>c0(7cbv4De-(LyEcWVf}=479BaEL$gQ=bVJJj8Ht%A_iq&hA8}WTt-;T zijDaslKu|;_w>3vdU$B$t~;}vV>VuhpaaJ(F2lB)i};1%` zO@H3l!kCJP#$mY~m?+Bc*S#BqK-p;<&wzjx@iRLUkw@>o0gpfR#so`%$ zbX4Sa4+4Xs!9YRbEeq59lOl@weLi({`S@h)c-;(zwviUccgI!A!#f~ZlRW}=teBoXM|>i%z93NDAF$))Kk#n@E?Cg zNSbT7k4ASVQx;4Kk|Xvh+_uKaEerZ`6m3KUG6|1pNQkgLx?n^|RSEjcOyD?>as_mf zmmvsKKnmok1p59BE0iriK$_w`0*TWAAS2s%#oMrl3%(}LwY3mpgS#x0$vL~XmduTN z;j4!bHoRok_DqQFVkdP(3vf~7`_~@V4@EMFMw8apq?(sksPWc~Ug0pwFeiS1%AmP( z&3A$T?HUP3B5bZmX5|FV6lB?kz!UlIOo0Fg8+^L&WMx|yAH2M#$^5g@x)SE#-TRgafk}IdKj2 zh!ZJm5v*eXep81Kj?KTxwo2e6^~xS_};J zBlHQ-5_{&GK0%6hedrmYwg)mJRgPKqY;LF_z*HnNn0|>+nIlj*0`>jTV}(X!&Q+53;Lg<2zfZtkLmDj$fMsAI%-R5$ z95_J|18r3R52M2MMNyFCX3{y@y4T}=E4)*$S=?u`5CfJCD)h!7VeKldz!>P6cMyAc zd>5s***`lIZJ0bTW;6m`J*mOQ%BpZ-@Gpr+o?xJp=_d@fDO$lJ81tNOY z^Li_&(U!{J{V%|Yw{iw;ilE^dMF4dz%o;|u4A@MtdAum^xHAZ!!|Il_O!Gm&?!Dg; zdWxvK;XE^i2e$<10ZP3R<4{D-@Lut=Au*}E4KxHPdu-i)e;`?O3Q;S<>&-u!B7yKp z7<8ek+VCQxfKf{vz$?@(w;&h;jS4Ub+%w_E{30|tQpt@!lo#a}jR1oVk~@fjx1VR; zdM>7=3cuY=660+vf@-tqJox4l(8I!>v9m6?{a!sw%iS|r&|C9)#z8Y5^j-llI>c8% z4e0V_6V);Grtvng0rKo5p-z)py^n5 zK(MF|gfOr`x0T1DC{JrGyqKMVn!+E*48%1F4V$R~R*}rM1R7V2b1r*?4)*}?!6JwV zL2hL_UvwOilfWCFo6dRxvcH^$w#7@sTY6oHTLXfW9aoLhS#6HpcRp&bUE@-gqf-j2 z2>TN0sA)nP_bsc#T>~43IZu&OQ30 z1>pG#douaSUjXx(hg%Zwb|jTlsjWToWz0t5gVnUQwh{JuBJK+GxId|@6%LEyrV9wR z#dp(cXta8vRR&}^CzcD)xsOd3DeKNj@#!ZN%8cuoJ%Yt2uSca_H&zb?5`zQUFy#UmRDQIVO1ua6zF-dWG%(kOy{IDxs&Axj ziTpSejx+6`*WK1If~60yo4A)Z%%TB$}*wR!ih9t^A=O%MbvvSWePjT`r} z?OC8}L@+N}!{tvL%7d)ThUGjrGb>_k0i_BsCW{sO%t-uWLcZm~mwOD>#m42b?M~AR zK8k&#o;Mtv5-f|i^!Ok~pF83Uwl%8K6TJ_n!>#BrNX8%`j)6D&50aE4jzL4$p50*`CtM>icC7zr>rO4z zhMFF&Lx=X~nQ-n@c$j?aR)`TvTKtI-2@!V1>y8oj#UsuU zn)!2%jz#lz`73KKd}o;|D-M^aJIxF-^BT?;X4_7AzIgH6>+R3LT2qC2N*rT|YM5o) zpj6Jtcz<(iOSxi%!>QY;cU3xD8v|ckj#l`OJo4)2MdNSlvGNt(rOu0NL zN!%#E3Y$mJh6S&=5d3HP(DnDwyOjF6gIr+6Tv)%jV*%DqZr} zNubtWZ{S_2Yhdw{#d|tBHKD~{_V2`8f0A}IhBDcDw!$-q)Uz_HA5ZdKpK32$>u_Sz zS08#i$?W`fgna21iSM-I!^@i!`NPin_lnY8N~*OEVZQLM9f8^!Q=Y0fk`g37sV*lrM8Ts4(Z55H<{R`<%XdIZpr7$&+Rw5+k49 z%;yu&RPn+oq4tp?*1b(}Yqupf=AW!d&Nf#isO56Cn%m_bf5PPTfp8scNTt5;5Cz3v za4x8~;ZJX})m z`y#GThLU(cS=-Exjrl;abvw0s%|0gQ^~xtA7fm&tPdnOOhWU@`lUTcREyC`yjDYi@ z$)~euS*}IcN_Q>zs4JeY$r3u1Yu{|>>_9E9H=k2rIDd@yI3{Y!>HwN@_ThY7*D>uP z2epU9hdQ@PmuZBouXr=?i(en>kJJga(CXQ0=0Ll526L-}+3#={GDatyE^R{~vq zekh8aDIVd*SlEGAjS#28#23q+NPO?@ES73b?tNXn^W4o4&YYd|bF+gF+Do{j9Tqzh zv%7&jI?7my(5!nQK8BR*L~?7LE8EK-xG6*3v2SL|?v+*B4nmY|nXd9<_ceQyo z&h~6>D5&~2Pu9(l?t;gsY*rm*x5lT%#-4YXT^O`n3nvOp*)09?ytDM{UEvpWFOiHc zJy=>lbEges%Ne5UYpIK&YvI$y#OVr{%cKa$6Qs3+bZw;Auxn&)z9Hs4;^$T)EFC^N zEv+PcXzzZIOM*C{2Wu!PrT4I(=(zMEk^OCXZjxNBYmw;2NK=-0O0pT)cZOz8u{}XLj!^UCLAxE5CDg=c9kq zlEHjAye}T|?7`KA>7(g-8r0{^f{u0K3J%-Y6nkCVwMO0Lavsi4`g@QkkFO^s*()SG zf1u$0NS4E8nU*8fU?dQH+6S`Uu(->?-(zxeei^;FR?lQfDx8nWN|EfbClY5j*IZw0 zk|tcUR_7HZ7X4Pr4JN>2mRAWWUSmIs)INRhMbWYGy_9!H-m0B3!CXyzIOeJ=&0c!? z!EjuU=^}9lMvaS;^A)EbdzpXb5p>}!v(Cf1`iow^t_7?lEteb>26O|(%k4TV#9^&S zQNiNg)y5uzuaN5K?ORmodIH_N!jYU{PzzIQ`pJ@!CAEgHYNtX|hRV3|aoSX_lR8=#Fkh6m<|pFFWOOu*xWxr8?yJ6Wu=0>N2;=)|!dOZP zc>DkhPZ}W}ml~th(w>|!`l*~@Y($u>k*Rz=N6tYU(^t_k*W)5i6l+?8LFIRzjTC7L z8@3xc3HB$aak=F0Ld*sAjh83D&C!oC`=8@xDvxYE{0tKRPc#bjb72mdF8Voczf})k zVE+a72`A|A^|~Eo-TI{U&WTp@7Z>%3!iO+!D|@UDfwt0ZaOtJux}ix&K`GqSKGC)| z8hYVEI+o2jr>FPfh?QabhKuORuR3!_4tPS2fv~R2hw0mG0(voWB}tSR+L;ga zvBYt&lh&fr*UGh3ykABdvd1Wpz9+TDGZ<%V}Ve}9vgPOJBX%#Z7_iZfdL#$`7AiyhcmVc{MIzy$}$ zF`BK6xL)eRhoz1why{Sxqj=C&+*b#2-_s6E@!}3l-f84(N9Ocu>)>uQG#g$jL|E(k z>s^0oGa*cK0F(C-p-jMPlkx4?005xFJ)&YvYn6U`c40<8BL}=*F&@~8Zgx-fkqL-h zgXPXABGx=y&(dJ5v6(iDhY#~dk>V+2*@Yf`|AnR;up3xi0~?AEVXs?^TT8XWh_t&J zD}9!$=XD0-R4I02G@DvY#U8XZoM=o=sJLhS0PGk||0`Eg=2r|Rk9BL%_-;R+b8F%E zp@ZR<7|-UA|Lon!!B=S6AOC(2SRDrQ|G;?jXu(emnqV0K1ROei7_b5JlA{O;@I$ID zM++rwjKW&uaWdcU`KM#427P$ky zumH!(i%FZz12r!CGT?)1g`~=eBRep$7m!&>*A&i2tP|#kyPBrUnc-;BrP6|x{U-++ zu-{sXs2i#HNW*2JEcxu63+ra#j8FG0nJ>02Wxy9B*j`g;lLam+hrp=L#Dv(%lMm*; zb4O2pbdeL^45Zh3>H5)y^W@1Q0D3yVzU*Khvfr2t)|Ocu&m@}}$rVAlasRh5h29)N z1Sv=}i@Xwq@eReLu)i?J$>yBIL79kQ3w2)$Lq*GN^d_ys3hvr6q(;quC9%tFBb- z673w^6Z9hyR@V;zRQLo(4ltsj%vrsNkZ}0^k(R(XyPSWlE0T1h`9=RoWQIjYNEbtN zPEO9fgZ^G|M~7k_eE&7ia`FMx7c3{EaWjf5EW0onENH0@X0N`tkrU_7h2I#Ly1<>8GJL)2&n`Lu{Y54j%$|U>xG> zuSaf{TCk2!C|Lj7^%gw*Vu{@ylGQ7J&RQAxoaLvqw3bdvA)(*y&akL22*Cv6Z9;OA z)MWiyU%lBxnuk>e8%dG_0uYpIa<(%?Ld?A#X^zCH`GFm7LUaIgWn=)APTcpsyVxal zjKPsLcSTmcx88TCt@VHe?iW8}uhaGBV^jR>(4FD1C`Ge79+H&mjet&d>N=OC*m}XS z?BeyRR9MDu@2Sf_d#Glto-?wY_6*glH?$0>`1DSCU`ZrfCQje?$qX|D5my&O&C?DR zhMSUT9%1R#)+f=h3ykDt1g1Eb9xR5eM`#~D>=)ld14YkyQx_8}6-AQxF&?$1fsy(` z8`~t+SFKQe;@?YEX}5=fuL-JZ)5crROkduGDK~%s@Bln>FkYoTL$udm^J20kwdS2q z@dooc(>ZHJPo{P~I@aavm?VXR=!X#GAwj|X`I!-1?V4>;y(N@M3RdpyZ{=iuj!+#| z;MV-fgxk1UUkX)h<&336tVCOA{Y#WNqt%rfE$d$brZ^7Bpy6Frxdy5UL?&O>uw7+_ ze2O4{Qhx@Nb4~@`FM=GVV%s)Cdu73p(=2}^%@S^!d&e`WhiJb|wg1cKMM4;#AZjTx9lc*?!#ywttR4AP zLAI;w*GG=EEXHvHM~?6mp1&=*=5$&kBUVARXD8R5n;zO7%UFW;P3?N~ot-mYf*PZ(gE{;!d+0s+PA5OQ;QFC0gtkmXjec$;ANTbR-|8wWWDv5>t+Xw8pbys z-4{GLz^z?$+Li3G%*{RqC{7L^CR0ruE@FZ|lBHQe5LJ?Nc$iNz%k6y^?R3kF+h{K( z!|PtTGq{sh#A#ARK}8V;5Rn9d(3Z9tL;lAKCXkkVHa%9{Ca?VV{C1^c?fRY2w+Mf1BTv|(kJQ9e*iKimwKie*^NcAcI;;gA%#I*Zf&k#pgv0Qr9fj!#rD>tOsXRD1-LVZW;`M5^*K&7$a`|PiddONB5~3SL`5>c8n8RW z;d5(?WVia&hh~l{atEq%H4o^akEtyFc}%zET2HtzEzpst95VTaKp2z98mp=+mPHkb zJOi>2U?7#7T~9s&Ww3N9z4%9J-418a6lGUu_g9$ZD?vh^g1em+{Y#rg#52uvRyq!Q zh|EAw|CYTT2Cn^*MO2Z7O(^9-Xz;jcdy4TcM_%_+-%M9ierzImu5Z<@Capox%eEpR z?T0G;zEnUqv*47Gm85=tYm%1Z9V!B4HO@DkY)x$woBCre=|8JN-t^G_dfEdR+EHiY4O2p@_tF=(l8AM6- zYSpV4@G1}Sst#MD2-J~}mN;v}gksQa;Rf}C*TT0E$-$)|)-*l`WPDPonQZay7@!pkJ;oXY(A-vf)QrP;!V(ZD}Mw>aLZK z#TldKeQ;Qi@G-CQ?Q>*F?vEFMFyj;EMBd5gRu#Ukl{7Xsjg1zO<(bDxoP@dHVUXQK z0*p)z1aYQow~_P}b9u9;A6ux5Pw54Yx9RIv@qJS}n4CMyTv%cA*22M}+@8`;suF&L z{k+B=S%~~Y83EAPKw0%WzYPP!6f{_xV`u6X275r3;R=KZ7zhLfD@Q~5dX<;65huGl zd~y2N>Z*Oa(C5INHwgmQ04CR^N)o|LdO$gy9th^-B%5Yg+m zXy~)oMNX_shddiBAx0kqOV?f_&08{WCMqJMH5C!cwfxpkCH=)kojK=58oXVWG0EvD z501Gw(1$#7bo3krk!l2adB2+F{8%&a0LdV@`*w(ug)eW$jBSHVCnb zUIJ?-)hkx4UV>#FL>jvDQxx0Gxze8Y6-47zY0t*U_&Bth5zIZgKqJy19|{Fg8!+XU zz?&_|URD5VMAnsi0_WzrLQhVBo_y~wdVm615wGh|>^@OBc^w4SWOEt`=sbFN0<>BD zx@W%K;lp0Gkg3n`jJ7tm4r?>{!8TspJ6-3r($J#Bq~HhpEJT$d_4nQuYiTdXGMfg^ zz}0v=?T|<=)H00zI__q4V__}{ysCgfZs1CogCCcGi3kuWt-W{?=TLp(4MW!<9I^_l zE(g?fo-eIi7RU>CIYo2_r59CLKuX`46B?*7@oA`!O4%_r6^np3fq*bEkk!+!&`ZpP zI{46(r+rvQS8pPz!Z4ioe$sBZrTBj!JHLU^vtJ8_{qacm!>YmAy8FY}|7@Ya zt@Y?NJ7<5#N+XQZtmgjV>vgkpc6QFHdlvS4t`7L~&Dlwnh}&}Ds+&&38*+-);_}V* zStR^F9?d=4$3=uRHQGYWuQS27|kQ`CUWl zg|!bdcaR{Y1IxPwcf1G(S$bmO;?`|xID+lvMlv{R0y}ujFYa|PU~%A{*+Wz#eFd` zk@LAlv5$sj+FG3@P2Z8@n#Q#ZEGNbwtEjAEE1GyWh|xB z0}tp4I-kq@!p~n%%}Dms#%^3G(dIY3kWhm8vZEU}7T^_K-KX=)s=t9}dP;tP9Z44K zi1T&uA79KYPAzQkwsB6p`*saxYsg@~?kp fKFATkDd)bUPae4Zun!)a1*?lz6jJdX*QkF1Yocb` literal 0 HcmV?d00001 diff --git a/jdk_25_maven/cs/rest/jasper/docs/origins.png b/jdk_25_maven/cs/rest/jasper/docs/origins.png new file mode 100644 index 0000000000000000000000000000000000000000..2721832e5d1a84e8f113746d2290173f09b5aaed GIT binary patch literal 105951 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 literal 0 HcmV?d00001 diff --git a/jdk_25_maven/cs/rest/jasper/docs/release-notes/jasper-1.0.md b/jdk_25_maven/cs/rest/jasper/docs/release-notes/jasper-1.0.md new file mode 100644 index 000000000..7536816ca --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/docs/release-notes/jasper-1.0.md @@ -0,0 +1,62 @@ +# v1.0 Release Notes + +## Definitions + +Most of the interface will be familiar to anyone who has used reddit, but how it actually works is +quite different. + +**Refs:** In Jasper, almost everything is either a ref or a tag. This text post you are reading is a +ref. Link posts are refs, and so are comments. Refs can have sources added, which can be used to form +a graph. To maintain a directed graph, all sources must have a prior publication +date. See this [cheatsheet](https://jfcere.github.io/ngx-markdown/cheat-sheet) for what markdown +is allowed. + +**Tags:** There are three kinds of tags, each defined by their own semantics: Public, protected, and +private tags. Private tags start with an underscore `_`, protected tags start with a plus sign `+`, and public tags have no prefix at all. Except for their prefixes, tags must only contain lowercase letters and forward slahses "/". Private tags may only be seen or used if you have access. Protected tags may only be used if you have access. Public tags are... totally public. + +**Users:** Users are special tags that are either protected or private. They are all prefixed with +"+user/" or "_user/". Public users are disallowed since it would be possible to spoof comments. Each +user has a list of read-access and write-access tags. Any tag in your read access list gives you +access to read any ref tagged by it. Similarly for write. Protected tags are special in that read +access to a protected tag gives you access to use that tag. + +**Special Tags:** There are also three special tags. +* `public` give everyone read access +* `locked` prevents any future editing by anyone +* `internal` prevents a ref from showing up on the home page or tag page + +**Roles:** There are three roles users may have that give escalating levels of access: +* User Role: Anyone who can log in gains this role. You may post new refs, and tag according to your +user permissions. +* Mod Role: You can see and edit all tags and refs on the server (except for locked refs). You can +create new Feeds and Tag Extensions. You can assign permissions to any user. You have access to the modlist. +* Admin Role: all the permissions of the Mod Role, but you can also install Plugins and Templates. + +**Exts:** Tag Extensions (Exts) are additional functionalty attached to a tag. The default tag +extension allows for pinning Refs to the top of the tag page, and customizing the title and sidebar. See Templates for how tags can be further extended. + +**Feeds:** Mods can create RSS feeds and assign tags for content they ingest. + +**Plugins:** Admins can create Plugins that extend Jasper's functionality. Usually these also +require modifying the user interface. Plugins include things such as comment threads, direct +messages, LaTeX, crypto invoices, and QR codes. + +**Templates:** Admins can create templates that change how Exts work based on their tag prefix. +`user/` is a template for a User Ext that allows users to subscribe to what tags are visible on +their home page. Another template is `queue/` which allows creating work queues and managing +invoices. + +## Work in Progress + +**Queues:** Work queues need some work to be more user friendly. Feedback welcome. + +**Graph:** The graph plugin lets you visualize the connections between Refs in a graph format. +Currently in a rough state, feedback welcome. + +**User Management:** Will add SCIM integration to manage users in any SCIM compatible auth server. + +**Remote Origins:** For now, Jasper only works as a single instance. + +**Theming:** Allowing custom theming is still in progress. Currently there is a dark theme and a +light theme, but there just uses whichever is default for your system. I will add a toggle button +in a few days. diff --git a/jdk_25_maven/cs/rest/jasper/docs/release-notes/jasper-1.1.md b/jdk_25_maven/cs/rest/jasper/docs/release-notes/jasper-1.1.md new file mode 100644 index 000000000..5a0e193ae --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/docs/release-notes/jasper-1.1.md @@ -0,0 +1,51 @@ +# v1.1 Release Notes + +This release of Jasper has built on the previous release, adding the Editor role and modified +how the Tag Based Access Control (TBAC) works. + +**Users:** +* In addition to the list of read-access and write-access tags, there is now list of tag-specific +read/write access. These only let you query and see that a tag exists (read) and allow modifying the +tag's Ext (write). +* You can now add a bookmark to a Query without adding it to your home page. + +**Roles:** There are four roles users may have that give escalating levels of access: +* User Role: Anyone who can log in gains this role. You may post new refs, and tag according to your +user permissions. +* Editor Role: You can tag any visible ref with any public tag. You may edit the Ext for any public +tag. You may create new Feeds. +* Mod Role: You can see and edit all tags and refs on the server (except for locked refs). You can +create new Feeds and Tag Extensions. You can assign permissions to any user. You have access to the +modlist. +* Admin Role: all the permissions of the Mod Role, but you can also install Plugins and Templates. + +**Editing**: There are some new shortcuts and embeds available to make editing easier. +* Any links where the text for the link is a number or a number in square brackets[2] +will be automaticly added to the Ref's sources. Similarly with alternate URLs when the text is +like[alt1]. When using +[reference style links](https://jfcere.github.io/ngx-markdown/cheat-sheet#links) the reference name +must be a number (or alt#). +* Public tags starting with a hash sign will be added to the Ref's tags like `#jasper`. This will +also render a clickable link to the tag page. +* User tags will notify the user when this post is submitted or replied to. This will also render a +clickable link to the user tag page. Like so: +user/chris +* Using wiki-style links like `[[Jasper]]` will link to the wiki page of that name like +* All editors now have live markdown preview + + +**Embedding**: You can now embed any Ref, Query, audio, video, or image in another Ref. +* Drag any image or video to resize it. +* Any links to Refs, queries, audio, video, image will display an embed toggle button to expand +them +* A link with the special text "ref" will just show the Ref and not show the link, like so +`[ref](/ref/url)` +* Links to embeddable content also work, does not have to be a Ref, like so +`[video](https://i.imgur.com/uYMg9nK.mp4)` +* A link with the special text "embed" will just embed the contents of the Ref, like so +`[embed](/ref/url)` +* A link with the special text "query" will just embed the results of a tag query, like so +`[query](jasper)` + +**Curity Integration**: Jasper is now integrated with Curity using SCIM. You can change your +password (if you've logged in within the last 15 minutes) on the settings page `/settings/password`. +Admins can create, delete, activate, deactivate, set role, and change the password of all users. diff --git a/jdk_25_maven/cs/rest/jasper/docs/release-notes/jasper-1.2.md b/jdk_25_maven/cs/rest/jasper/docs/release-notes/jasper-1.2.md new file mode 100644 index 000000000..1fa19ed3b --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/docs/release-notes/jasper-1.2.md @@ -0,0 +1,25 @@ +# v1.2 Release Notes + +## Server + +* Added `admin` profile for running locally. Treats every request as from an admin user + with user tag `+user/admin` +* Added backup / restore functionality +* Added replication functionality +* Made (origin, modified) a unique key in all tables +* Hierarchical tags now added for all tags, not just public tags +* Removed Feed entity. Now use built-in plugin `+plugin/feed` +* Removed Origin entity. Now use built-in plugin `+plugin/origin` +* Added distinct modified date to Metadata +* Added ROLE_VIEWER to allow authenticated read-only access +* Added linux/arm64 docker platform to support Apple Silicon + +## Reference Client + +* Added Ref action to view source if user does not have write permission +* Show asterix next to created date if Ref has been edited +* Added custom theme editor at the tag and user level +* Editor adds all links to sources unless they are alts, instead of requiring source format `[1](url)` +* Sources using `[1](url)` format and alts using `[alt1](url)` format are renumbered to match correct index +* Sources missing url `[1]` or `[[1]` will have their url added. Similarly for alts +* Added support for using `^` as superscript diff --git a/jdk_25_maven/cs/rest/jasper/gatling/.dockerignore b/jdk_25_maven/cs/rest/jasper/gatling/.dockerignore new file mode 100644 index 000000000..5bc29ef12 --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/gatling/.dockerignore @@ -0,0 +1,9 @@ +# Development artifacts +target/ +report/ +.DS_Store + +# Scripts and local dev files +docker-compose.yaml +.dockerignore +.gitignore diff --git a/jdk_25_maven/cs/rest/jasper/gatling/.gitignore b/jdk_25_maven/cs/rest/jasper/gatling/.gitignore new file mode 100644 index 000000000..b41cbc825 --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/gatling/.gitignore @@ -0,0 +1,2 @@ +target/ +report/ diff --git a/jdk_25_maven/cs/rest/jasper/gatling/Dockerfile b/jdk_25_maven/cs/rest/jasper/gatling/Dockerfile new file mode 100644 index 000000000..b8cfdc084 --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/gatling/Dockerfile @@ -0,0 +1,11 @@ +FROM maven:3.9.11-amazoncorretto-25-debian as builder +WORKDIR /gatling +COPY pom.xml . +RUN mvn -B clean package -Dmaven.main.skip -Dmaven.test.skip -Dcodegen.skip && rm -r target + +FROM builder as test +WORKDIR /gatling +COPY src ./src/ +CMD mvn gatling:test -Dgatling.simulationClass=simulations.jasper.${GATLING_TEST:-SmokeTest}Simulation; \ + mkdir -p /report && \ + cp -r target/gatling/* ./report/ diff --git a/jdk_25_maven/cs/rest/jasper/gatling/README.md b/jdk_25_maven/cs/rest/jasper/gatling/README.md new file mode 100644 index 000000000..92b69bf54 --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/gatling/README.md @@ -0,0 +1,142 @@ +# Jasper Load Testing + +This module contains comprehensive load testing for the Jasper application using Gatling. + +## Overview + +The load testing has been separated into its own module to isolate Gatling dependencies from the main application. This resolves Netty version conflicts that occur when upgrading Gatling. + +## Test Scenarios + +This module now includes four comprehensive test scenarios: + +### 1. SimpleJasperSimulation +Basic smoke test that verifies core functionality: +- Basic CRUD operations on references +- Extension and plugin management +- User and system health checks +- Quick validation of all major components + +### 2. ComprehensiveJasperSimulation +Realistic knowledge management workflows: +- Knowledge workers creating and organizing content +- Content browsers searching and consuming information +- Administrators managing system configuration +- Heavy readers with intensive content consumption +- Power users with mixed operations including content enrichment + +### 3. UserJourneySimulation +Real-world user journey patterns: +- **Research Sessions**: Users researching topics, saving references, taking notes +- **Daily Reviews**: Users checking recent updates and organizing content +- **Content Curation**: Users creating and managing templates and plugins +- **Collaborative Work**: Multiple users working on shared content +- Includes realistic data feeders for topics and sources + +### 4. StressTestSimulation +System limits and edge case testing: +- High-volume concurrent operations +- Large payloads and complex queries +- Error conditions and recovery scenarios +- Replication and backup operations +- System administration under load +- **Retry Logic**: All requests automatically retry up to 3 times on 429 (Too Many Requests) and 503 (Service Unavailable) errors + +### 5. InfernoSimulation +Extreme load testing to push system to absolute limits: +- 15 second warmup trickle to let the system initialize +- One cycle of massive load (50,000 users ramped in 10 seconds, then 5 users/sec for 50 seconds) +- One cycle of massive load (5,000 users ramped in 10 seconds, then 5 users/sec for 50 seconds) +- 2 minute 15 second total duration +- Tests extreme concurrent operations and system recovery +- **Very Low Success Threshold**: Only >15% success rate required (85% failure tolerance) + +## Running Load Tests + +To run all scenarios: + +```bash +cd gatling +mvn gatling:test +``` + +To run from the root directory: + +```bash +mvn -f gatling/pom.xml gatling:test +``` + +To run a specific simulation: + +```bash +mvn gatling:test -Dgatling.simulationClass=simulations.jasper.SimpleJasperSimulation +``` + +To run the Inferno extreme load test: + +```bash +mvn gatling:test -Dgatling.simulationClass=simulations.jasper.InfernoSimulation +``` + +## Docker + +The load tests can also be run via Docker using the `gatling` stage: + +```bash +docker build --target gatling -t jasper-gatling . +docker run jasper-gatling +``` + +Or run a specific test with environment variable: + +```bash +docker build --target test -t jasper-gatling . +docker run -e GATLING_TEST=Inferno jasper-gatling +``` + +Or using the docker-compose file: + +```bash +docker compose --profile lt -f gatling/docker-compose.yaml up --build --exit-code-from gatling +``` + +## Configuration + +- **Gatling Version**: 3.14.5 +- **Target URL**: http://localhost:8081 (configurable in simulation files) +- **Simulations**: Located in `src/test/java/simulations/jasper/` + +## Performance Expectations + +Each simulation includes assertions and performance expectations: + +- **SimpleJasperSimulation**: Quick smoke test (45 seconds), >85% success rate +- **ComprehensiveJasperSimulation**: Comprehensive test (3 minutes), >80% success rate +- **UserJourneySimulation**: Realistic workflows (5 minutes), >75% success rate +- **StressTestSimulation**: System limits test (4 minutes), >70% success rate +- **InfernoSimulation**: Extreme load test (2 minutes 15 seconds), >15% success rate + +## Adding New Tests + +Create new simulation classes in `src/test/java/simulations/jasper/` that extend `Simulation` from Gatling. + +## Real-World Scenarios Included + +The enhanced test suite includes: +- Knowledge workers adding references with metadata +- Search and filtering operations with complex queries +- Plugin and template management workflows +- Graph relationship queries +- Content enrichment via proxy and scraping +- User management and authentication flows +- Backup and replication operations +- Error handling and edge cases +- Concurrent operations and race conditions + +## Dependencies + +This module uses its own dependencies isolated from the main application: +- Gatling Highcharts 3.14.5 +- Netty 4.2.6.Final (via Gatling) + +This isolation prevents dependency conflicts with the main Spring Boot application. diff --git a/jdk_25_maven/cs/rest/jasper/gatling/docker-compose.sqlite.yaml b/jdk_25_maven/cs/rest/jasper/gatling/docker-compose.sqlite.yaml new file mode 100644 index 000000000..b64d0e24b --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/gatling/docker-compose.sqlite.yaml @@ -0,0 +1,47 @@ +services: + gatling: + build: . + profiles: + - lt + network_mode: host + depends_on: + web: + condition: service_healthy + repl-web: + condition: service_healthy + environment: + GATLING_TEST: "${GATLING_TEST:-SmokeTest}" + volumes: + - ./report:/gatling/report + web: + build: .. + ports: + - 8081:80 + healthcheck: + test: "curl -f http://localhost/management/health/readiness" + environment: + SPRING_LOGGING_LEVEL_ROOT: WARN + SPRING_PROFILES_ACTIVE: prod,sqlite,storage,scripts,proxy + SERVER_PORT: 80 + JASPER_DEFAULT_ROLE: ROLE_ADMIN + JASPER_OVERRIDE_SERVER_MAX_REQUESTS: 1 + JASPER_OVERRIDE_SERVER_MAX_CONCURRENT_REQUESTS: 100 + JASPER_HEAP: 512m + JASPER_GC: "${JASPER_GC:-g1gc}" + JASPER_STORAGE: /var/lib/jasper + repl-web: + build: .. + ports: + - 8083:80 + healthcheck: + test: "curl -f http://localhost/management/health/readiness" + environment: + SPRING_LOGGING_LEVEL_ROOT: WARN + SPRING_PROFILES_ACTIVE: prod,sqlite,scripts + SERVER_PORT: 80 + JASPER_LOCAL_ORIGIN: '@repl' + JASPER_DEFAULT_ROLE: ROLE_ADMIN + JASPER_OVERRIDE_SERVER_WEB_ORIGINS: '@repl' + JASPER_HEAP: 256m + JASPER_GC: "${JASPER_GC:-g1gc}" + JASPER_STORAGE: /var/lib/jasper diff --git a/jdk_25_maven/cs/rest/jasper/gatling/docker-compose.yaml b/jdk_25_maven/cs/rest/jasper/gatling/docker-compose.yaml new file mode 100644 index 000000000..456b26a72 --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/gatling/docker-compose.yaml @@ -0,0 +1,67 @@ +services: + gatling: + build: . + profiles: + - lt + network_mode: host + depends_on: + web: + condition: service_healthy + repl-web: + condition: service_healthy + environment: + GATLING_TEST: "${GATLING_TEST:-SmokeTest}" + volumes: + - ./report:/gatling/report + web: + build: .. + ports: + - 8081:80 + healthcheck: + test: "curl -f http://localhost/management/health/readiness" + environment: + SPRING_LOGGING_LEVEL_ROOT: WARN + SPRING_PROFILES_ACTIVE: prod,storage,scripts,proxy + SPRING_DATASOURCE_URL: jdbc:postgresql://db/jasper + SPRING_DATASOURCE_USERNAME: jasper + SPRING_DATASOURCE_PASSWORD: jasper + SERVER_PORT: 80 + JASPER_DEFAULT_ROLE: ROLE_ADMIN + JASPER_OVERRIDE_SERVER_MAX_REQUESTS: 1 + JASPER_OVERRIDE_SERVER_MAX_CONCURRENT_REQUESTS: 100 + JASPER_HEAP: 512m + JASPER_GC: "${JASPER_GC:-g1gc}" + depends_on: + - db + db: + image: postgres + environment: + POSTGRES_USER: jasper + POSTGRES_PASSWORD: jasper + POSTGRES_DB: jasper + repl-web: + build: .. + ports: + - 8083:80 + healthcheck: + test: "curl -f http://localhost/management/health/readiness" + environment: + SPRING_LOGGING_LEVEL_ROOT: WARN + SPRING_PROFILES_ACTIVE: prod,scripts + SPRING_DATASOURCE_URL: jdbc:postgresql://repl-db/jasper + SPRING_DATASOURCE_USERNAME: jasper + SPRING_DATASOURCE_PASSWORD: jasper + SERVER_PORT: 80 + JASPER_LOCAL_ORIGIN: '@repl' + JASPER_DEFAULT_ROLE: ROLE_ADMIN + JASPER_OVERRIDE_SERVER_WEB_ORIGINS: '@repl' + JASPER_HEAP: 256m + JASPER_GC: "${JASPER_GC:-g1gc}" + depends_on: + - repl-db + repl-db: + image: postgres + environment: + POSTGRES_USER: jasper + POSTGRES_PASSWORD: jasper + POSTGRES_DB: jasper diff --git a/jdk_25_maven/cs/rest/jasper/gatling/gen-index.sh b/jdk_25_maven/cs/rest/jasper/gatling/gen-index.sh new file mode 100644 index 000000000..9937dd18a --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/gatling/gen-index.sh @@ -0,0 +1,195 @@ +#!/bin/bash + +# Generate index page for all Gatling reports +REPORT_DIR="./report" +INDEX_FILE="$REPORT_DIR/index.html" + +# Create report directory if it doesn't exist +mkdir -p "$REPORT_DIR" + +# Start HTML +cat > "$INDEX_FILE" << 'EOF' + + + + + + Jasper Load Test Reports + + + + +

+

🚀 Jasper Knowledge Management Server - Load Test Reports

+ +

📊 Test Scenarios

+
+EOF + +# Define simulation information +declare -A simulations=( + ["SmokeTestSimulation"]="Smoke Test|Quick validation of core functionality (45s, >75% success)" + ["ComprehensiveSimulation"]="Comprehensive Test|Realistic knowledge management workflows (3min, >75% success)" + ["UserJourneySimulation"]="User Journey Test|Real user journey patterns with research and collaboration (5min, >75% success)" + ["StressTestSimulation"]="Stress Test|System limits and edge cases testing (4min, >70% success)" + ["InfernoSimulation"]="Inferno Test|Extreme load test with warmup and 2 cycles of massive ramp (2min 15s, >15% success)" +) + +# Check for reports and generate cards +for simulation in SmokeTestSimulation ComprehensiveSimulation UserJourneySimulation StressTestSimulation InfernoSimulation; do + IFS='|' read -r title description <<< "${simulations[$simulation]}" + # Find the most recent report directory for this simulation + report_dir=$(find $REPORT_DIR -name "${simulation,,}*" -type d 2>/dev/null | head -1) + cat >> "$INDEX_FILE" << EOF +
+
$title
+
$description
+EOF + if [ -n "$report_dir" ] && [ -f "$report_dir/index.html" ]; then + cat >> "$INDEX_FILE" << EOF + 📈 View Report +EOF + else + cat >> "$INDEX_FILE" << EOF + ❌ Report not available +EOF + fi + cat >> "$INDEX_FILE" << EOF +
+EOF +done + +# Complete HTML +cat >> "$INDEX_FILE" << EOF +
+ +

📋 Test Summary

+

These load tests validate the Jasper Knowledge Management Server under various scenarios:

+
    +
  • Simple: Basic smoke test for CI/CD validation
  • +
  • Comprehensive: Full workflow testing with realistic user patterns
  • +
  • User Journey: End-to-end user experience validation
  • +
  • Stress: Performance limits and concurrent user testing
  • +
+ + +
+ + +EOF + +echo "✅ Gatling reports index generated at: $INDEX_FILE" + +# List available reports +echo "📁 Available reports:" +find "$REPORT_DIR" -name "index.html" | sed 's|/report/||' | sort diff --git a/jdk_25_maven/cs/rest/jasper/gatling/pom.xml b/jdk_25_maven/cs/rest/jasper/gatling/pom.xml new file mode 100644 index 000000000..96e8ecdee --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/gatling/pom.xml @@ -0,0 +1,61 @@ + + + 4.0.0 + jasper + jasper-load-testing + 1.3.0-SNAPSHOT + jasper-load-testing + Load testing for Jasper application using Gatling + + + 25 + ${java.version} + ${java.version} + ${java.version} + UTF-8 + UTF-8 + 4.21.10 + 3.15.1 + + + + + io.gatling.highcharts + gatling-charts-highcharts + ${gatling.version} + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.15.0 + + true + ${java.version} + ${java.version} + ${java.version} + + + + io.gatling + gatling-maven-plugin + ${gatling-maven-plugin.version} + + + --add-opens=java.base/java.lang=ALL-UNNAMED + + true + true + + simulations.jasper.*Simulation + + + + + + diff --git a/jdk_25_maven/cs/rest/jasper/gatling/src/test/gatling/conf/gatling.conf b/jdk_25_maven/cs/rest/jasper/gatling/src/test/gatling/conf/gatling.conf new file mode 100644 index 000000000..b35d6d941 --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/gatling/src/test/gatling/conf/gatling.conf @@ -0,0 +1,132 @@ +######################### +# Gatling Configuration # +######################### + +# This file contains all the settings configurable for Gatling with their default values + +gatling { + core { + #outputDirectoryBaseName = "" # The prefix for each simulation result folder (then suffixed by the report generation timestamp) + #runDescription = "" # The description for this simulation run, displayed in each report + #encoding = "utf-8" # Encoding to use throughout Gatling for file and string manipulation + #simulationClass = "" # The FQCN of the simulation to run (when used in conjunction with noReports, the simulation for which assertions will be validated) + #mute = false # When set to true, don't ask for simulation name nor run description (currently only used by Gatling SBT plugin) + #elFileBodiesCacheMaxCapacity = 200 # Cache size for request body EL templates, set to 0 to disable + #rawFileBodiesCacheMaxCapacity = 200 # Cache size for request body Raw templates, set to 0 to disable + #rawFileBodiesInMemoryMaxSize = 1000 # Below this limit, raw file bodies will be cached in memory + + extract { + regex { + #cacheMaxCapacity = 200 # Cache size for the compiled regexes, set to 0 to disable caching + } + xpath { + #cacheMaxCapacity = 200 # Cache size for the compiled XPath queries, set to 0 to disable caching + } + jsonPath { + #cacheMaxCapacity = 200 # Cache size for the compiled jsonPath queries, set to 0 to disable caching + #preferJackson = false # When set to true, prefer Jackson over Boon for JSON-related operations + } + css { + #cacheMaxCapacity = 200 # Cache size for the compiled CSS selectors queries, set to 0 to disable caching + } + } + + directory { + #data = user-files/data # Folder where user's data (e.g. files used by Feeders) is located + #bodies = user-files/bodies # Folder where bodies are located + #simulations = user-files/simulations # Folder where the bundle's simulations are located + #reportsOnly = "" # If set, name of report folder to look for in order to generate its report + #binaries = "" # If set, name of the folder where compiles classes are located: Defaults to GATLING_HOME/target. + #results = results # Name of the folder where all reports folder are located + } + } + charting { + #noReports = false # When set to true, don't generate HTML reports + #maxPlotPerSeries = 1000 # Number of points per graph in Gatling reports + #useGroupDurationMetric = false # Switch group timings from cumulated response time to group duration. + indicators { + #lowerBound = 800 # Lower bound for the requests' response time to track in the reports and the console summary + #higherBound = 1200 # Higher bound for the requests' response time to track in the reports and the console summary + #percentile1 = 50 # Value for the 1st percentile to track in the reports, the console summary and Graphite + #percentile2 = 75 # Value for the 2nd percentile to track in the reports, the console summary and Graphite + #percentile3 = 95 # Value for the 3rd percentile to track in the reports, the console summary and Graphite + #percentile4 = 99 # Value for the 4th percentile to track in the reports, the console summary and Graphite + } + } + http { + #fetchedCssCacheMaxCapacity = 200 # Cache size for CSS parsed content, set to 0 to disable + #fetchedHtmlCacheMaxCapacity = 200 # Cache size for HTML parsed content, set to 0 to disable + #perUserCacheMaxCapacity = 200 # Per virtual user cache size, set to 0 to disable + #warmUpUrl = "http://gatling.io" # The URL to use to warm-up the HTTP stack (blank means disabled) + #enableGA = true # Very light Google Analytics, please support + ssl { + keyStore { + #type = "" # Type of SSLContext's KeyManagers store + #file = "" # Location of SSLContext's KeyManagers store + #password = "" # Password for SSLContext's KeyManagers store + #algorithm = "" # Algorithm used SSLContext's KeyManagers store + } + trustStore { + #type = "" # Type of SSLContext's TrustManagers store + #file = "" # Location of SSLContext's TrustManagers store + #password = "" # Password for SSLContext's TrustManagers store + #algorithm = "" # Algorithm used by SSLContext's TrustManagers store + } + } + ahc { + #keepAlive = true # Allow pooling HTTP connections (keep-alive header automatically added) + #connectTimeout = 10000 # Timeout when establishing a connection + #handshakeTimeout = 10000 # Timeout when performing TLS hashshake + #pooledConnectionIdleTimeout = 60000 # Timeout when a connection stays unused in the pool + #readTimeout = 60000 # Timeout when a used connection stays idle + #maxRetry = 2 # Number of times that a request should be tried again + #requestTimeout = 60000 # Timeout of the requests + #acceptAnyCertificate = true # When set to true, doesn't validate SSL certificates + #httpClientCodecMaxInitialLineLength = 4096 # Maximum length of the initial line of the response (e.g. "HTTP/1.0 200 OK") + #httpClientCodecMaxHeaderSize = 8192 # Maximum size, in bytes, of each request's headers + #httpClientCodecMaxChunkSize = 8192 # Maximum length of the content or each chunk + #webSocketMaxFrameSize = 10240000 # Maximum frame payload size + #sslEnabledProtocols = [TLSv1.2, TLSv1.1, TLSv1] # Array of enabled protocols for HTTPS, if empty use the JDK defaults + #sslEnabledCipherSuites = [] # Array of enabled cipher suites for HTTPS, if empty use the AHC defaults + #sslSessionCacheSize = 0 # SSLSession cache size, set to 0 to use JDK's default + #sslSessionTimeout = 0 # SSLSession timeout in seconds, set to 0 to use JDK's default (24h) + #useOpenSsl = false # if OpenSSL should be used instead of JSSE (requires tcnative jar) + #useNativeTransport = false # if native transport should be used instead of Java NIO (requires netty-transport-native-epoll, currently Linux only) + #tcpNoDelay = true + #soReuseAddress = false + #soLinger = -1 + #soSndBuf = -1 + #soRcvBuf = -1 + #allocator = "pooled" # switch to unpooled for unpooled ByteBufAllocator + #maxThreadLocalCharBufferSize = 200000 # Netty's default is 16k + } + dns { + #queryTimeout = 5000 # Timeout of each DNS query in millis + #maxQueriesPerResolve = 6 # Maximum allowed number of DNS queries for a given name resolution + } + } + jms { + #acknowledgedMessagesBufferSize = 5000 # size of the buffer used to tracked acknowledged messages and protect against duplicate receives + } + data { + #writers = [console, file] # The list of DataWriters to which Gatling write simulation data (currently supported : console, file, graphite, jdbc) + console { + #light = false # When set to true, displays a light version without detailed request stats + } + file { + #bufferSize = 8192 # FileDataWriter's internal data buffer size, in bytes + } + leak { + #noActivityTimeout = 30 # Period, in seconds, for which Gatling may have no activity before considering a leak may be happening + } + graphite { + #light = false # only send the all* stats + #host = "localhost" # The host where the Carbon server is located + #port = 2003 # The port to which the Carbon server listens to (2003 is default for plaintext, 2004 is default for pickle) + #protocol = "tcp" # The protocol used to send data to Carbon (currently supported : "tcp", "udp") + #rootPathPrefix = "gatling" # The common prefix of all metrics sent to Graphite + #bufferSize = 8192 # GraphiteDataWriter's internal data buffer size, in bytes + #writeInterval = 1 # GraphiteDataWriter's write interval, in seconds + } + } +} diff --git a/jdk_25_maven/cs/rest/jasper/gatling/src/test/gatling/conf/logback.xml b/jdk_25_maven/cs/rest/jasper/gatling/src/test/gatling/conf/logback.xml new file mode 100644 index 000000000..189bcdab8 --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/gatling/src/test/gatling/conf/logback.xml @@ -0,0 +1,24 @@ + + + + + + + + %d{HH:mm:ss.SSS} [%-5level] %logger{15} - %msg%n%rEx + false + + + + + + + + + + + + + + + diff --git a/jdk_25_maven/cs/rest/jasper/gatling/src/test/java/simulations/jasper/ComprehensiveSimulation.java b/jdk_25_maven/cs/rest/jasper/gatling/src/test/java/simulations/jasper/ComprehensiveSimulation.java new file mode 100644 index 000000000..46c5cb554 --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/gatling/src/test/java/simulations/jasper/ComprehensiveSimulation.java @@ -0,0 +1,471 @@ +package simulations.jasper; + +import io.gatling.javaapi.core.*; +import io.gatling.javaapi.http.*; +import static io.gatling.javaapi.core.CoreDsl.*; +import static io.gatling.javaapi.http.HttpDsl.*; + +import java.time.Duration; +import java.util.List; +import java.util.Map; + +/** + * Comprehensive Jasper Knowledge Management Load Test + * + * This simulation represents real-world usage patterns: + * - Knowledge workers adding and organizing references + * - Users browsing and searching content + * - Plugin-based content enrichment + * - Template-based content organization + * - User management operations + */ +public class ComprehensiveSimulation extends Simulation { + private static final String STATIC_XSRF_TOKEN = "gatling-static-token-for-testing"; + + HttpProtocolBuilder httpProtocol = http + .baseUrl("http://localhost:8081") + .acceptHeader("application/json") + .contentTypeHeader("application/json") + .userAgentHeader("Gatling Load Test - Comprehensive"); + + // ====================== Reference Operations ====================== + + ChainBuilder createWebReference = + exec(session -> session.set("webRefUrl", "https://example.com/article-" + System.currentTimeMillis() + "-" + new java.util.Random().nextInt(10000))) + .exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Create Web Reference") + .post("/api/v1/ref") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .body(StringBody(""" + { + "url": "#{webRefUrl}", + "title": "Knowledge Article #{randomInt(1,1000)}", + "comment": "Important reference for our research", + "tags": ["research", "article", "knowledgebase"], + "sources": ["https://source.example.com"] + }""")) + .check(status().in(201, 409)) + ) + .pause(Duration.ofMillis(200)) + .exec( + http("Verify Created Web Reference") + .get("/api/v1/ref") + .queryParam("url", "#{webRefUrl}") + .check(status().is(200)) + ).pause(Duration.ofMillis(300)); + + ChainBuilder createBookReference = + exec(session -> session.set("bookRefUrl", "isbn:978-" + String.format("%09d", new java.util.Random().nextInt(1000000000)) + new java.util.Random().nextInt(10))) + .exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Create Book Reference") + .post("/api/v1/ref") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .body(StringBody(""" + { + "url": "#{bookRefUrl}", + "title": "Technical Book #{randomInt(1,100)}", + "comment": "Reference book on software engineering", + "tags": ["book", "technical", "software"] + }""")) + .check(status().in(201, 409)) + ) + .pause(Duration.ofMillis(200)) + .exec( + http("Verify Created Book Reference") + .get("/api/v1/ref") + .queryParam("url", "#{bookRefUrl}") + .check(status().is(200)) + ).pause(Duration.ofMillis(300)); + + ChainBuilder createComment = + exec(session -> session.set("commentUrl", "comment:" + java.util.UUID.randomUUID().toString())) + .exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Create Comment Reference") + .post("/api/v1/ref") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .body(StringBody(""" + { + "url": "#{commentUrl}", + "title": "User Comment #{randomInt(1,500)}", + "comment": "This is a user comment or note", + "tags": ["comment", "discussion"], + "sources": ["#{webRefUrl}"] + }""")) + .check(status().in(201, 409)) + ) + .pause(Duration.ofMillis(200)) + .exec( + http("Verify Created Comment") + .get("/api/v1/ref") + .queryParam("url", "#{commentUrl}") + .check(status().is(200)) + ).pause(Duration.ofMillis(100)); + + // ====================== Browse and Search Operations ====================== + + ChainBuilder browseRecentRefs = exec( + http("Browse Recent References") + .get("/api/v1/ref/page") + .queryParam("size", "20") + .queryParam("sort", "modified,desc") + .check(status().is(200)) + .check(jsonPath("$.content").exists()) + ).pause(Duration.ofSeconds(1)); + + ChainBuilder searchByTag = exec( + http("Search by Tag") + .get("/api/v1/ref/page") + .queryParam("query", "research") + .queryParam("size", "10") + .check(status().is(200)) + ).pause(Duration.ofMillis(800)); + + ChainBuilder searchByKeyword = exec( + http("Search by Keyword") + .get("/api/v1/ref/page") + .queryParam("search", "technical software") + .queryParam("size", "15") + .check(status().is(200)) + ).pause(Duration.ofMillis(800)); + + ChainBuilder getSpecificRef = + exec(session -> session.set("lookupRefUrl", "https://example.com/article-" + System.currentTimeMillis() + "-" + new java.util.Random().nextInt(10000))) + .exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Create Ref for Lookup") + .post("/api/v1/ref") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .body(StringBody(""" + { + "url": "#{lookupRefUrl}", + "title": "Lookup Article #{randomInt(1,1000)}", + "comment": "Article for lookup test", + "tags": ["test", "lookup"] + }""")) + .check(status().in(201, 409)) + ) + .pause(Duration.ofMillis(100)) + .exec( + http("Get Specific Reference") + .get("/api/v1/ref") + .queryParam("url", "#{lookupRefUrl}") + .check(status().is(200)) + ).pause(Duration.ofMillis(400)); + + // ====================== Extension Operations ====================== + + ChainBuilder createExtensionTemplate = exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Create Extension Template") + .post("/api/v1/template") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .body(StringBody(""" + { + "tag": "test.ext", + "name": "Test Extension Template", + "schema": {} + }""")) + .check(status().in(201, 409)) + ).pause(Duration.ofMillis(100)); + + ChainBuilder createPlugin = exec(session -> { + int randomId = new java.util.Random().nextInt(50) + 1; + return session.set("testExtTag", "test.ext/" + randomId); + }) + .exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Create Plugin Extension") + .post("/api/v1/ext") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .body(StringBody(""" + { + "tag": "#{testExtTag}", + "name": "Test Extension", + "config": { + "description": "A test extension for load testing", + "version": "1.0.0", + "enabled": true + } + }""")) + .check(status().in(201, 409)) + ).pause(Duration.ofMillis(600)); + + ChainBuilder browseExtensions = exec( + http("Browse Extensions") + .get("/api/v1/ext/page") + .queryParam("size", "20") + .check(status().is(200)) + ).pause(Duration.ofMillis(700)); + + // ====================== Plugin Management ====================== + + ChainBuilder createPluginConfig = + exec(session -> session.set("pluginTag", "+plugin/custom." + (1 + new java.util.Random().nextInt(30)))) + .exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Create Plugin Configuration") + .post("/api/v1/plugin") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .body(StringBody(""" + { + "tag": "#{pluginTag}", + "name": "Custom Plugin #{randomInt(1,30)}", + "config": { + "type": "viewer", + "title": "Custom Viewer", + "selector": "custom-selector" + } + }""")) + .check(status().in(201, 409)) + ) + .pause(Duration.ofMillis(200)) + .exec( + http("Verify Created Plugin") + .get("/api/v1/plugin") + .queryParam("tag", "#{pluginTag}") + .check(status().is(200)) + ).pause(Duration.ofMillis(300)); + + ChainBuilder getPluginConfig = exec( + http("Get Plugin Configuration") + .get("/api/v1/plugin") + .queryParam("tag", "#{pluginTag}") + .check(status().is(200)) + ).pause(Duration.ofMillis(300)); + + // ====================== Template Operations ====================== + + ChainBuilder createTemplate = + exec(session -> session.set("templateTag", "_template/article." + (1 + new java.util.Random().nextInt(20)))) + .exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Create Template") + .post("/api/v1/template") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .body(StringBody(""" + { + "tag": "#{templateTag}", + "name": "Article Template #{randomInt(1,20)}", + "config": { + "description": "Template for organizing article references", + "category": "content" + }, + "defaults": { + "title": "", + "summary": "", + "category": "tech" + }, + "schema": { + "properties": { + "title": {"type": "string"} + }, + "optionalProperties": { + "summary": {"type": "string"}, + "category": {"enum": ["tech", "business", "research"]} + } + } + }""")) + .check(status().in(201, 409)) + ) + .pause(Duration.ofMillis(200)) + .exec( + http("Verify Created Template") + .get("/api/v1/template") + .queryParam("tag", "#{templateTag}") + .check(status().is(200)) + ).pause(Duration.ofMillis(400)); + + ChainBuilder browseTemplates = exec( + http("Browse Templates") + .get("/api/v1/template/page") + .queryParam("size", "10") + .check(status().is(200)) + ).pause(Duration.ofMillis(500)); + + // ====================== User Management ====================== + + ChainBuilder createUser = exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Create User") + .post("/api/v1/user") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .body(StringBody(""" + { + "tag": "+user/testuser#{randomInt(1,100)}", + "name": "Test User #{randomInt(1,100)}", + "config": { + "role": "USER", + "active": true + } + }""")) + .check(status().in(201, 409)) + ).pause(Duration.ofMillis(400)); + + ChainBuilder getUserInfo = exec( + http("Get User Info") + .get("/api/v1/user/whoami") + .check(status().is(200)) + ).pause(Duration.ofMillis(300)); + + // ====================== Graph and Analytics ====================== + + ChainBuilder getGraphData = exec( + http("Get Graph Data") + .get("/api/v1/graph/list") + .queryParam("urls", List.of( + "https://example.com/article-1", + "https://example.com/article-2" + )) + .check(status().is(200)) + ).pause(Duration.ofMillis(800)); + + ChainBuilder browseTaggedContent = exec( + http("Browse Tagged Content") + .get("/api/v1/ref/page") + .queryParam("query", "research") + .queryParam("size", "20") + .check(status().is(200)) + ).pause(Duration.ofMillis(600)); + + // ====================== Content Enrichment ====================== + + ChainBuilder proxyContent = + // Create a ref for proxy if webRefUrl not set + doIf(session -> !session.contains("webRefUrl")).then( + exec(session -> session.set("webRefUrl", "https://cjmalloy.github.io/jasper/docs/entities.png?q=#{randomInt(1,1000)}")) + .exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Create Ref for Proxy") + .post("/api/v1/ref") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .body(StringBody(""" + { + "url": "#{webRefUrl}", + "title": "Proxy Test Article", + "comment": "Article for proxy test", + "tags": ["proxy", "test"] + }""")) + .check(status().in(201, 409)) + ) + .pause(Duration.ofMillis(100)) + ) + .exec( + http("Proxy External Content") + .get("/api/v1/proxy") + .queryParam("url", "#{webRefUrl}") + .check(status().is(200)) + ).pause(Duration.ofMillis(1000)); + + // ====================== Scenarios ====================== + + // Knowledge Worker: Creates and organizes content + ScenarioBuilder knowledgeWorker = scenario("Knowledge Worker") + .exec(createWebReference) + .pause(Duration.ofSeconds(2)) + .exec(createBookReference) + .pause(Duration.ofSeconds(1)) + .exec(createComment) + .pause(Duration.ofSeconds(3)) + .exec(searchByTag) + .pause(Duration.ofSeconds(2)) + .exec(createTemplate) + .pause(Duration.ofSeconds(1)) + .exec(browseRecentRefs); + + // Content Browser: Searches and views existing content + ScenarioBuilder contentBrowser = scenario("Content Browser") + .exec(browseRecentRefs) + .pause(Duration.ofSeconds(1)) + .exec(searchByKeyword) + .pause(Duration.ofSeconds(2)) + .exec(getSpecificRef) + .pause(Duration.ofSeconds(1)) + .exec(searchByTag) + .pause(Duration.ofSeconds(3)) + .exec(getGraphData) + .pause(Duration.ofSeconds(1)) + .exec(browseTaggedContent); + + // Administrator: Manages system configuration + ScenarioBuilder administrator = scenario("Administrator") + .exec(createPluginConfig) + .pause(Duration.ofSeconds(2)) + .exec(getPluginConfig) + .pause(Duration.ofSeconds(1)) + .exec(browseExtensions) + .pause(Duration.ofSeconds(2)) + .exec(createUser) + .pause(Duration.ofSeconds(1)) + .exec(getUserInfo) + .pause(Duration.ofSeconds(3)) + .exec(browseTemplates); + + // Heavy Reader: Intensive content consumption + ScenarioBuilder heavyReader = scenario("Heavy Reader") + .repeat(3).on( + exec(browseRecentRefs) + .pause(Duration.ofMillis(500)) + .exec(searchByKeyword) + .pause(Duration.ofMillis(800)) + .exec(getSpecificRef) + .pause(Duration.ofMillis(300)) + ) + .exec(proxyContent); + + // Power User: Mixed operations including content enrichment + ScenarioBuilder powerUser = scenario("Power User") + .exec(createExtensionTemplate) + .exec(createPlugin) + .pause(Duration.ofSeconds(1)) + .exec(createWebReference) + .pause(Duration.ofSeconds(2)) + .exec(proxyContent) + .pause(Duration.ofSeconds(1)) + .exec(searchByTag) + .pause(Duration.ofSeconds(2)) + .exec(getGraphData); + + { + setUp( + // Knowledge workers - moderate activity + knowledgeWorker.injectOpen( + nothingFor(Duration.ofSeconds(5)), + rampUsers(10).during(Duration.ofSeconds(30)), + constantUsersPerSec(5).during(Duration.ofSeconds(60)) + ), + // Content browsers - high read activity + contentBrowser.injectOpen( + nothingFor(Duration.ofSeconds(2)), + rampUsers(20).during(Duration.ofSeconds(20)), + constantUsersPerSec(10).during(Duration.ofSeconds(80)) + ), + // Administrators - low activity + administrator.injectOpen( + nothingFor(Duration.ofSeconds(10)), + rampUsers(3).during(Duration.ofSeconds(15)), + constantUsersPerSec(1).during(Duration.ofSeconds(60)) + ), + // Heavy readers - burst activity + heavyReader.injectOpen( + nothingFor(Duration.ofSeconds(15)), + atOnceUsers(15), + constantUsersPerSec(8).during(Duration.ofSeconds(45)) + ), + // Power users - sporadic activity + powerUser.injectOpen( + nothingFor(Duration.ofSeconds(8)), + rampUsers(5).during(Duration.ofSeconds(25)), + constantUsersPerSec(2).during(Duration.ofSeconds(50)) + ) + ).protocols(httpProtocol) + .maxDuration(Duration.ofMinutes(3)) + .assertions( + global().responseTime().max().lt(5000), + global().successfulRequests().percent().gt(75.0), // Adjusted for auth-protected endpoints + forAll().responseTime().percentile3().lt(3000) + ); + } +} diff --git a/jdk_25_maven/cs/rest/jasper/gatling/src/test/java/simulations/jasper/InfernoSimulation.java b/jdk_25_maven/cs/rest/jasper/gatling/src/test/java/simulations/jasper/InfernoSimulation.java new file mode 100644 index 000000000..147ee07bb --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/gatling/src/test/java/simulations/jasper/InfernoSimulation.java @@ -0,0 +1,109 @@ +package simulations.jasper; + +import io.gatling.javaapi.core.*; +import io.gatling.javaapi.http.*; +import static io.gatling.javaapi.core.CoreDsl.*; +import static io.gatling.javaapi.http.HttpDsl.*; + +import java.time.Duration; + +/** + * Inferno Load Test for Jasper + * + * This simulation pushes the system to its absolute limits: + * - 15 second warmup trickle to let the system initialize + * - Two cycles of extreme load (ramp huge users for 10s, trickle for 50s) + * - 2 minute 15 second total duration + * - >15% success rate required to pass (85% failure tolerance) + * - Tests extreme concurrent operations and recovery + */ +public class InfernoSimulation extends Simulation { + private static final String STATIC_XSRF_TOKEN = "gatling-static-token-for-testing"; + + HttpProtocolBuilder httpProtocol = http + .baseUrl("http://localhost:8081") + .acceptHeader("application/json") + .contentTypeHeader("application/json") + .userAgentHeader("Gatling Inferno Test") + .disableFollowRedirect() + .check(status().not(500)); // Allow other error codes for extreme stress testing + + // ====================== High Volume Operations ====================== + + ChainBuilder rapidRefCreation = + exec(session -> { + // Add timestamp to URL for uniqueness to avoid duplicate key violations + String url = "https://inferno-test.example.com/" + + System.currentTimeMillis() + "-" + + (1 + new java.util.Random().nextInt(1000000)); + return session.set("rapidRefUrl", url); + }) + .exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Inferno Ref Creation") + .post("/api/v1/ref") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .body(StringBody(""" + { + "url": "#{rapidRefUrl}", + "title": "Inferno Test #{randomInt(1,1000000)}", + "comment": "InfernoTest#{randomInt(100000,999999)} - Extreme stress test", + "tags": [ + "inferno", + "extreme", + "load.#{randomInt(1,10000)}" + ] + }""")) + .check(status().in(429, 503, 201, 409, 400)) + ) + .pause(Duration.ofMillis(10), Duration.ofMillis(50)); + + ChainBuilder rapidQuery = exec( + http("Inferno Query") + .get("/api/v1/ref/page") + .queryParam("size", "100") + .queryParam("query", "inferno") + .check(status().in(429, 503, 200, 502, 504)) + ) + .pause(Duration.ofMillis(10), Duration.ofMillis(100)); + + ChainBuilder searchQuery = exec( + http("Inferno Search") + .get("/api/v1/ref/page") + .queryParam("query", "extreme|load.#{randomInt(1,1000)}") + .queryParam("size", "50") + .check(status().in(429, 503, 200, 502, 504)) + ) + .pause(Duration.ofMillis(20), Duration.ofMillis(100)); + + // ====================== Scenarios ====================== + + ScenarioBuilder infernoLoad = scenario("Inferno Load") + .randomSwitch().on( + percent(50.0).then(exec(rapidRefCreation)), + percent(30.0).then(exec(rapidQuery)), + percent(20.0).then(exec(searchQuery)) + ); + + // ====================== Simulation Setup ====================== + + { + setUp( + // Warmup: 15 second intro trickle to let the system initialize + infernoLoad.injectOpen( + constantUsersPerSec(5).during(Duration.ofSeconds(15)), + // Cycle 1: Ramp huge number of users for 10 seconds, then trickle for 50 seconds + rampUsers(20_000).during(Duration.ofSeconds(10)), + constantUsersPerSec(5).during(Duration.ofSeconds(50)), + // Cycle 2: Another huge ramp for 10 seconds, then trickle for 50 seconds + rampUsers(5_000).during(Duration.ofSeconds(10)), + constantUsersPerSec(5).during(Duration.ofSeconds(50)) + ) + ).protocols(httpProtocol) + .maxDuration(Duration.ofMinutes(3)) + .assertions( + global().responseTime().max().lt(30000), // Allow very long response times + global().successfulRequests().percent().gt(15.0) // Only 15% success required + ); + } +} diff --git a/jdk_25_maven/cs/rest/jasper/gatling/src/test/java/simulations/jasper/SmokeTestSimulation.java b/jdk_25_maven/cs/rest/jasper/gatling/src/test/java/simulations/jasper/SmokeTestSimulation.java new file mode 100644 index 000000000..27ad42271 --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/gatling/src/test/java/simulations/jasper/SmokeTestSimulation.java @@ -0,0 +1,222 @@ +package simulations.jasper; + +import io.gatling.javaapi.core.*; +import io.gatling.javaapi.http.*; +import static io.gatling.javaapi.core.CoreDsl.*; +import static io.gatling.javaapi.http.HttpDsl.*; + +import java.time.Duration; + +/** + * Simple Jasper Smoke Test + * + * Basic smoke test to verify core functionality: + * - Create references + * - Browse and search content + * - Test core API endpoints + * - Verify basic system health + */ +public class SmokeTestSimulation extends Simulation { + private static final String STATIC_XSRF_TOKEN = "gatling-static-token-for-testing"; + + HttpProtocolBuilder httpProtocol = http + .baseUrl("http://localhost:8081") + .acceptHeader("application/json") + .contentTypeHeader("application/json") + .userAgentHeader("Gatling Smoke Test") + .check(status().not(500)); + + // ====================== Basic Operations ====================== + + ChainBuilder getAllRefs = exec( + http("Get All Refs") + .get("/api/v1/ref/page") + .queryParam("size", "10") + .check(status().is(200)) + .check(jsonPath("$.content").exists()) + ).pause(Duration.ofSeconds(1)); + + ChainBuilder createWebReference = + exec(session -> session.set("testUrl", "https://example.com/test-" + System.currentTimeMillis() + "-" + new java.util.Random().nextInt(10000))) + .exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Create Web Reference") + .post("/api/v1/ref") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .body(StringBody(""" + { + "url": "#{testUrl}", + "title": "Test Article #{randomInt(1,1000)}", + "comment": "This is a test reference for smoke testing", + "tags": ["test", "smoketest", "example"] + }""")) + .check(status().is(201)) + .check(jsonPath("$").saveAs("createdRefTimestamp")) + ) + .pause(Duration.ofMillis(500)) + .exec( + http("Verify Created Reference") + .get("/api/v1/ref") + .queryParam("url", "#{testUrl}") + .check(status().is(200)) + ).pause(Duration.ofMillis(500)); + + ChainBuilder getSpecificRef = exec( + http("Get Specific Reference") + .get("/api/v1/ref") + .queryParam("url", "#{testUrl}") + .check(status().is(200)) + ).pause(Duration.ofMillis(500)); + + ChainBuilder searchRefs = exec( + http("Search References") + .get("/api/v1/ref/page") + .queryParam("query", "test") + .queryParam("size", "5") + .check(status().is(200)) + ).pause(Duration.ofMillis(800)); + + ChainBuilder countRefs = exec( + http("Count References") + .get("/api/v1/ref/count") + .queryParam("query", "smoketest") + .check(status().is(200)) + ).pause(Duration.ofMillis(500)); + + // ====================== Extension Operations ====================== + + ChainBuilder browseExtensions = exec( + http("Browse Extensions") + .get("/api/v1/ext/page") + .queryParam("size", "10") + .check(status().is(200)) + ).pause(Duration.ofMillis(600)); + + ChainBuilder createTestExtension = exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Create Test Extension") + .post("/api/v1/ext") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .body(StringBody(""" + { + "tag": "smoketest.#{randomInt(1,100)}", + "name": "Smoke Test Extension #{randomInt(1,100)}" + }""")) + .check(status().in(201, 409)) // 201 Created or 409 Conflict if already exists + ).pause(Duration.ofMillis(700)); + + // ====================== Plugin Operations ====================== + + ChainBuilder browsePlugins = exec( + http("Browse Plugins") + .get("/api/v1/plugin/page") + .queryParam("size", "10") + .check(status().is(200)) + ).pause(Duration.ofMillis(500)); + + // ====================== Template Operations ====================== + + ChainBuilder browseTemplates = exec( + http("Browse Templates") + .get("/api/v1/template/page") + .queryParam("size", "10") + .check(status().is(200)) + ).pause(Duration.ofMillis(500)); + + // ====================== User Operations ====================== + + ChainBuilder getUserInfo = exec( + http("Get User Info") + .get("/api/v1/user/whoami") + .check(status().is(200)) + ).pause(Duration.ofMillis(400)); + + ChainBuilder browseUsers = exec( + http("Browse Users") + .get("/api/v1/user/page") + .queryParam("size", "10") + .check(status().is(200)) + ).pause(Duration.ofMillis(500)); + + // ====================== System Health ====================== + + ChainBuilder checkOrigins = exec( + http("Check Origin System") + .get("/api/v1/origin") + .check(status().is(200)) + ).pause(Duration.ofMillis(300)); + + // ====================== Scenarios ====================== + + ScenarioBuilder basicFunctionality = scenario("Basic Functionality Test") + .exec(getAllRefs) + .exec(createWebReference) + .exec(getSpecificRef) + .exec(searchRefs) + .exec(countRefs); + + ScenarioBuilder systemComponents = scenario("System Components Test") + .exec(browseExtensions) + .exec(createTestExtension) + .exec(browsePlugins) + .exec(browseTemplates); + + ScenarioBuilder userAndSystem = scenario("User and System Test") + .exec(getUserInfo) + .exec(browseUsers) + .exec(checkOrigins); + + ScenarioBuilder comprehensiveSmoke = scenario("Comprehensive Smoke Test") + .exec(getAllRefs) + .pause(Duration.ofSeconds(1)) + .exec(createWebReference) + .pause(Duration.ofSeconds(1)) + .exec(searchRefs) + .pause(Duration.ofSeconds(1)) + .exec(browseExtensions) + .pause(Duration.ofSeconds(1)) + .exec(getUserInfo) + .pause(Duration.ofSeconds(1)) + .repeat(2).on( + exec(getAllRefs) + .pause(Duration.ofMillis(500)) + .exec(getSpecificRef) + .pause(Duration.ofMillis(500)) + ); + + { + setUp( + // Basic functionality - core operations + basicFunctionality.injectOpen( + nothingFor(Duration.ofSeconds(1)), + atOnceUsers(3), + rampUsers(5).during(Duration.ofSeconds(10)) + ), + // System components - verify all components work + systemComponents.injectOpen( + nothingFor(Duration.ofSeconds(3)), + atOnceUsers(2), + rampUsers(3).during(Duration.ofSeconds(8)) + ), + // User and system health + userAndSystem.injectOpen( + nothingFor(Duration.ofSeconds(5)), + atOnceUsers(2), + constantUsersPerSec(1).during(Duration.ofSeconds(15)) + ), + // Comprehensive smoke test + comprehensiveSmoke.injectOpen( + nothingFor(Duration.ofSeconds(2)), + atOnceUsers(1), + rampUsers(4).during(Duration.ofSeconds(12)) + ) + ).protocols(httpProtocol) + .maxDuration(Duration.ofSeconds(45)) + .assertions( + global().responseTime().max().lt(5000), + global().responseTime().mean().lt(1500), + global().successfulRequests().percent().gt(75.0), // Adjusted for auth-protected endpoints + forAll().failedRequests().count().lt(50L) // Adjusted for realistic failure count in load testing + ); + } +} diff --git a/jdk_25_maven/cs/rest/jasper/gatling/src/test/java/simulations/jasper/StressTestSimulation.java b/jdk_25_maven/cs/rest/jasper/gatling/src/test/java/simulations/jasper/StressTestSimulation.java new file mode 100644 index 000000000..df23db9e2 --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/gatling/src/test/java/simulations/jasper/StressTestSimulation.java @@ -0,0 +1,454 @@ +package simulations.jasper; + +import io.gatling.javaapi.core.*; +import io.gatling.javaapi.http.*; +import static io.gatling.javaapi.core.CoreDsl.*; +import static io.gatling.javaapi.http.HttpDsl.*; + +import java.time.Duration; + +/** + * Stress Test and Edge Cases for Jasper + * + * This simulation tests system limits and edge cases: + * - High-volume concurrent operations + * - Large payloads and complex queries + * - Error conditions and recovery + * - Replication and backup scenarios + * - System administration operations + */ +public class StressTestSimulation extends Simulation { + private static final String STATIC_XSRF_TOKEN = "gatling-static-token-for-testing"; + + HttpProtocolBuilder httpProtocol = http + .baseUrl("http://localhost:8081") + .acceptHeader("application/json") + .contentTypeHeader("application/json") + .userAgentHeader("Gatling Stress Test") + .disableFollowRedirect() + .check(status().not(500)); // Allow other error codes for stress testing + + // ====================== High Volume Operations ====================== + + ChainBuilder rapidRefCreation = + exec(session -> { + // Add timestamp to URL for uniqueness to avoid duplicate key violations + String url = "https://stress-test.example.com/" + + System.currentTimeMillis() + "-" + + (1 + new java.util.Random().nextInt(100000)); + return session.set("rapidRefUrl", url); + }) + .exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Rapid Ref Creation") + .post("/api/v1/ref") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .body(StringBody(""" + { + "url": "#{rapidRefUrl}", + "title": "Stress Test Reference #{randomInt(1,100000)} - Load#{randomInt(10000,99999)}", + "comment": "StressTest#{randomInt(100000,999999)} - This is a stress test reference with substantial content to test system limits and performance under high load conditions. Content#{randomInt(100000,999999)}", + "tags": [ + "stresstest", + "performance", + "load.#{randomInt(1,1000)}", + "batch.#{randomInt(1,100)}", + "category.#{randomInt(1,50)}", + "priority.#{randomInt(1,10)}" + ], + "sources": [ + "https://source1.example.com/#{randomInt(1,1000)}", + "https://source2.example.com/#{randomInt(1,1000)}", + "https://source3.example.com/#{randomInt(1,1000)}" + ] + }""")) + .check(status().in(429, 503, 201, 409, 400)) + ) + .pause(Duration.ofMillis(50), Duration.ofMillis(200)); + + ChainBuilder largePageQuery = exec( + http("Large Page Query") + .get("/api/v1/ref/page") + .queryParam("size", "100") + .queryParam("query", "stresstest|performance|load.#{randomInt(1,100)}") + .queryParam("sort", "modified,desc") + .check(status().in(429, 503, 200)) + .check(responseTimeInMillis().lt(5000)) + ) + .pause(Duration.ofMillis(100), Duration.ofMillis(500)); + + ChainBuilder complexSearchQuery = + exec( + http("Complex Search Query") + .get("/api/v1/ref/page") + .queryParam("query", "(stresstest:performance)|load.#{randomInt(1,100)}:!excluded") + .queryParam("search", "stress test performance load content#{randomInt(1,100)}") + .queryParam("modifiedAfter", "2024-01-01T00:00:00Z") + .queryParam("size", "50") + .check(status().in(429, 503, 200)) + ) + .pause(Duration.ofMillis(200), Duration.ofMillis(800)); + + // ====================== Large Payload Operations ====================== + + ChainBuilder createLargeExtTemplate = exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Create Large Extension Template") + .post("/api/v1/template") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .body(StringBody(""" + { + "tag": "large.test", + "name": "Large Test Extension Template", + "schema": { + "properties": { + "type": {"type": "string"} + }, + "optionalProperties": { + "data": { + "elements": { + "properties": { + "id": {"type": "uint32"}, + "name": {"type": "string"}, + "description": {"type": "string"}, + "value": {"type": "float64"} + } + } + } + } + } + }""")) + .check(status().in(429, 503, 201, 409)) + ) + .pause(Duration.ofMillis(100)); + + ChainBuilder createLargeExt = exec(session -> { + // Build large config JSON + StringBuilder largeConfig = new StringBuilder("{\"type\":\"large-test\",\"data\":["); + for (int i = 0; i < 100; i++) { + if (i > 0) largeConfig.append(","); + largeConfig.append("{\"id\":").append(i) + .append(",\"name\":\"item").append(i).append("_").append(System.currentTimeMillis() % 10000) + .append("\",\"description\":\"desc").append(i).append("_").append(System.currentTimeMillis() % 100000) + .append("\",\"value\":").append(Math.random() * 1000).append("}"); + } + largeConfig.append("]}"); + + int randomId = (int)(Math.random() * 10000) + 1; + + String body = String.format(""" + { + "tag": "large.test/%d", + "name": "Large Test Extension %d", + "config": %s + }""", + randomId, + randomId, + largeConfig.toString()); + + return session.set("largeExtBody", body); + }) + .tryMax(3).on( + exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Create Large Extension") + .post("/api/v1/ext") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .body(StringBody("#{largeExtBody}")) + .check(status().in(429, 503, 201, 409)) + ) + ).exitHereIfFailed() + .pause(Duration.ofMillis(300), Duration.ofMillis(1000)); + + // ====================== Error Condition Testing ====================== + + ChainBuilder testNotFoundErrors = exec( + http("Test Not Found - Random URL") + .get("/api/v1/ref") + .queryParam("url", "https://nonexistent-#{randomInt(1,100000)}.example.com/#{randomUuid()}") + .check(status().in(429, 503, 404)) + ) + .pause(Duration.ofMillis(100), Duration.ofMillis(300)); + + ChainBuilder testInvalidData = exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Test Invalid Data") + .post("/api/v1/ref") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .body(StringBody(""" + { + "url": "invalid-url-test#{randomInt(1,1000)}", + "title": "", + "tags": ["invalidtag#{randomInt(100000,999999)}"] + }""")) + .check(status().in(429, 503, 400, 422)) + ) + .pause(Duration.ofMillis(100), Duration.ofMillis(300)); + + ChainBuilder testMalformedJson = exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Test Malformed JSON") + .post("/api/v1/ref") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .body(StringBody(""" + { + "url": "https://test.com/malformed", + "title": "Test" + "invalid": "json", + }""")) + .check(status().in(429, 503, 400)) + ) + .pause(Duration.ofMillis(100), Duration.ofMillis(300)); + + // ====================== Replication Testing ====================== + + ChainBuilder testReplicationEndpoints = exec( + http("Test Replication - Get Refs") + .get("/pub/api/v1/repl/ref") + .queryParam("size", "100") + .queryParam("modifiedAfter", "2024-01-01T00:00:00Z") + .check(status().in(429, 503, 200)) + ) + .pause(Duration.ofMillis(200), Duration.ofMillis(500)) + .exec( + http("Test Replication - Get Cursor") + .get("/pub/api/v1/repl/ref/cursor") + .check(status().in(429, 503, 200)) + ) + .pause(Duration.ofMillis(100), Duration.ofMillis(300)) + .exec( + http("Test Replication - Get Extensions") + .get("/pub/api/v1/repl/ext") + .queryParam("size", "50") + .check(status().in(429, 503, 200)) + ); + + // ====================== Backup Operations ====================== + + ChainBuilder testBackupOperations = exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Create Backup") + .post("/api/v1/backup") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .body(StringBody(""" + { + "includeRefs": true, + "includeExts": true, + "includeUsers": false, + "maxItems": 1000 + }""")) + .check(status().in(429, 503, 200)) // BackupController returns 200, not 201 (missing @ResponseStatus annotation) + ) + .pause(Duration.ofSeconds(2), Duration.ofSeconds(5)) + .exec( + http("List Backups") + .get("/api/v1/backup") + .check(status().in(429, 503, 200)) + ); + + // ====================== System Administration ====================== + + ChainBuilder testAdminOperations = exec( + http("Get User Info") + .get("/api/v1/user/whoami") + .check(status().in(429, 503, 200)) + ) + .pause(Duration.ofMillis(200), Duration.ofMillis(500)) + .exec( + http("Browse All Users") + .get("/api/v1/user/page") + .queryParam("size", "50") + .check(status().in(429, 503, 200)) + ); + + // ====================== Content Enrichment Stress ====================== + + ChainBuilder stressProxyOperations = exec( + http("Stress Proxy Operation") + .get("/api/v1/proxy") + .queryParam("url", "https://cjmalloy.github.io/jasper/docs/entities.png?q=#{randomInt(1,1000)}") + .check(status().in(429, 503, 200)) + .check(responseTimeInMillis().lt(10000)) + ) + .pause(Duration.ofMillis(500), Duration.ofMillis(2000)); + + ChainBuilder stressScrapeOperations = exec( + http("Stress Web Scraping") + .get("/api/v1/scrape/web") + .queryParam("url", "https://httpbin.org/html") + .check(status().in(429, 503, 200)) + ) + .pause(Duration.ofMillis(1000), Duration.ofMillis(3000)); + + // ====================== Concurrent Update Stress ====================== + + ChainBuilder concurrentUpdates = + exec(session -> { + // Use timestamp + random to make URLs unique per virtual user to avoid duplicate key violations + String url = "https://stress-test.example.com/shared-" + + System.currentTimeMillis() + "-" + + (1 + new java.util.Random().nextInt(10000)); + return session.set("stressUpdateUrl", url); + }) + // Create the ref for this specific virtual user + .exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Create Ref for Concurrent Update") + .post("/api/v1/ref") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .body(StringBody(""" + { + "url": "#{stressUpdateUrl}", + "title": "Stress Test Reference", + "comment": "Ref for testing concurrent updates", + "tags": ["stresstest", "concurrent"] + }""")) + .check(status().in(429, 503, 201)) + .check(jsonPath("$").optional().saveAs("stressRefModified")) + ) + .doIf(session -> session.contains("stressRefModified")).then( + exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Concurrent Update Attempt") + .patch("/api/v1/ref") + .queryParam("url", "#{stressUpdateUrl}") + .queryParam("cursor", "#{stressRefModified}") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .header("Content-Type", "application/merge-patch+json") + .body(StringBody(""" + { + "tags": ["updated.#{randomInt(1,1000)}", "concurrent.#{randomLong()}", "stressupdate"], + "comment": "Updated during stress test at #{randomLong()}" + }""")) + .check(status().in(429, 503, 200, 409)) + ) + ).pause(Duration.ofMillis(50), Duration.ofMillis(200)); + + // ====================== Graph Operations Stress ====================== + + ChainBuilder stressGraphOperations = exec(session -> { + // Build list of URLs for graph query + java.util.List urls = java.util.stream.IntStream.range(1, 21) + .mapToObj(i -> "https://stress-test.example.com/" + (System.currentTimeMillis() + i)) + .collect(java.util.stream.Collectors.toList()); + return session.set("graphUrls", urls); + }) + .exec( + http("Stress Graph Query") + .get("/api/v1/graph/list") + .multivaluedQueryParam("urls", "#{graphUrls}") + .check(status().in(429, 503, 200)) + .check(responseTimeInMillis().lt(8000)) + ) + .pause(Duration.ofMillis(300), Duration.ofMillis(1000)); + + // ====================== Scenarios ====================== + + ScenarioBuilder volumeStress = scenario("High Volume Operations") + .group("High Volume Operations").on( + repeat(20).on( + exec(rapidRefCreation) + .pace(Duration.ofMillis(100)) + ) + .exec(largePageQuery) + .exec(complexSearchQuery) + ); + + ScenarioBuilder errorHandlingTest = scenario("Error Handling Tests") + .group("Error Handling Tests").on( + repeat(10).on( + randomSwitch().on( + percent(40.0).then(exec(testNotFoundErrors)), + percent(30.0).then(exec(testInvalidData)), + percent(30.0).then(exec(testMalformedJson)) + ) + ) + ); + + ScenarioBuilder systemLimitsTest = scenario("System Limits Test") + .exec(createLargeExtTemplate) + .exec(createLargeExt) + .pause(Duration.ofSeconds(1), Duration.ofSeconds(3)) + .exec(stressGraphOperations) + .pause(Duration.ofSeconds(1), Duration.ofSeconds(2)) + .repeat(5).on( + exec(concurrentUpdates).pace(Duration.ofMillis(200)) + ); + + ScenarioBuilder replicationStress = scenario("Replication Stress") + .repeat(15).on( + exec(testReplicationEndpoints) + .pace(Duration.ofSeconds(1)) + ); + + ScenarioBuilder adminStress = scenario("Administration Stress") + .exec(testBackupOperations) + .pause(Duration.ofSeconds(2), Duration.ofSeconds(5)) + .repeat(8).on( + exec(testAdminOperations) + .pace(Duration.ofMillis(500)) + ); + + ScenarioBuilder contentEnrichmentStress = scenario("Content Enrichment Stress") + .repeat(10).on( + randomSwitch().on( + percent(60.0).then(exec(stressProxyOperations)), + percent(40.0).then(exec(stressScrapeOperations)) + ) + ); + + { + setUp( + // High volume concurrent operations + volumeStress.injectOpen( + nothingFor(Duration.ofSeconds(2)), + rampUsers(150).during(Duration.ofSeconds(30)), + constantUsersPerSec(60).during(Duration.ofMinutes(2)) + ), + // Error condition testing + errorHandlingTest.injectOpen( + nothingFor(Duration.ofSeconds(5)), + rampUsers(60).during(Duration.ofSeconds(20)), + constantUsersPerSec(30).during(Duration.ofMinutes(1)) + ), + // System limits testing + systemLimitsTest.injectOpen( + nothingFor(Duration.ofSeconds(10)), + rampUsers(45).during(Duration.ofSeconds(25)), + constantUsersPerSec(15).during(Duration.ofMinutes(2)) + ), + // Replication stress + replicationStress.injectOpen( + nothingFor(Duration.ofSeconds(8)), + rampUsers(20).during(Duration.ofSeconds(15)), + constantUsersPerSec(12).during(Duration.ofMinutes(1)) + ), + // Administration operations + adminStress.injectOpen( + nothingFor(Duration.ofSeconds(12)), + rampUsers(15).during(Duration.ofSeconds(20)), + constantUsersPerSec(6).during(Duration.ofMinutes(1)) + ), + // Content enrichment stress + contentEnrichmentStress.injectOpen( + nothingFor(Duration.ofSeconds(15)), + rampUsers(24).during(Duration.ofSeconds(30)), + constantUsersPerSec(12).during(Duration.ofMinutes(2)) + ) + ).protocols(httpProtocol) + .maxDuration(Duration.ofMinutes(4)) + .throttle( + reachRps(150).in(Duration.ofSeconds(30)), + holdFor(Duration.ofMinutes(2)), + jumpToRps(120), + holdFor(Duration.ofMinutes(1)) + ) + .assertions( + global().responseTime().max().lt(15000), + global().responseTime().mean().lt(3000), + global().successfulRequests().percent().gt(70.0), // Lower success rate expected for stress testing + details("High Volume Operations").failedRequests().percent().lt(40.0), + details("Error Handling Tests").responseTime().percentile3().lt(5000) + ); + } +} diff --git a/jdk_25_maven/cs/rest/jasper/gatling/src/test/java/simulations/jasper/UserJourneySimulation.java b/jdk_25_maven/cs/rest/jasper/gatling/src/test/java/simulations/jasper/UserJourneySimulation.java new file mode 100644 index 000000000..9bd00eafd --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/gatling/src/test/java/simulations/jasper/UserJourneySimulation.java @@ -0,0 +1,528 @@ +package simulations.jasper; + +import io.gatling.javaapi.core.*; +import io.gatling.javaapi.http.*; +import static io.gatling.javaapi.core.CoreDsl.*; +import static io.gatling.javaapi.http.HttpDsl.*; + +import java.time.Duration; +import java.util.List; + +/** + * Realistic User Journey Load Test for Jasper + * + * This simulation models realistic user workflows: + * - Research Session: User researches a topic, saves references, takes notes + * - Daily Review: User checks recent updates and organizes content + * - Content Curation: User creates and manages templates and plugins + * - Collaborative Work: Multiple users working on shared content + */ +public class UserJourneySimulation extends Simulation { + private static final String STATIC_XSRF_TOKEN = "gatling-static-token-for-testing"; + + HttpProtocolBuilder httpProtocol = http + .baseUrl("http://localhost:8081") + .acceptHeader("application/json") + .contentTypeHeader("application/json") + .userAgentHeader("Gatling Load Test - User Journey") + .check(status().not(500), status().not(502), status().not(503)); + + // ====================== Feeder Data ====================== + + FeederBuilder topicFeeder = csv("data/topics.csv").circular(); + + FeederBuilder sourceFeeder = csv("data/sources.csv").circular(); + + // ====================== Research Session Journey ====================== + + ChainBuilder researchWorkflow = feed(topicFeeder) + .exec(session -> { + System.out.println("User researching: " + session.getString("topic")); + // Normalize category to lowercase for use in tags + String category = session.getString("category").toLowerCase(); + return session.set("categoryTag", category); + }) + // Start by searching for existing knowledge + .exec( + http("Search Existing Knowledge - #{topic}") + .get("/api/v1/ref/page") + .queryParam("search", "#{topic}") + .queryParam("size", "20") + .check(status().is(200)) + .check(jsonPath("$.content").saveAs("existingRefs")) + ) + .pause(Duration.ofSeconds(2, 5)) + // Browse by category tag + .exec( + http("Browse by Category - #{category}") + .get("/api/v1/ref/page") + .queryParam("query", "#{categoryTag}") + .queryParam("size", "15") + .check(status().is(200)) + ) + .pause(Duration.ofSeconds(1, 3)) + // Create a new reference with research findings + .feed(sourceFeeder) + .exec(session -> { + String url = "https://" + session.getString("source") + "/" + session.getString("topic") + "-research-" + System.currentTimeMillis(); + return session.set("researchUrl", url); + }) + .exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Save Research Reference - #{topic}") + .post("/api/v1/ref") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .body(StringBody(""" + { + "url": "#{researchUrl}", + "title": "#{topic} Research - Study#{randomInt(1000,9999)}", + "comment": "Research findings on #{topic} from #{source}", + "tags": ["research", "#{categoryTag}", "#{type}"] + }""")) + .check(status().is(201)) + .check(jsonPath("$").saveAs("createdTimestamp")) + ) + .pause(Duration.ofMillis(500)) + // Verify the created reference + .exec( + http("Verify Created Research Reference") + .get("/api/v1/ref") + .queryParam("url", "#{researchUrl}") + .check(status().is(200)) + .check(jsonPath("$.url").isEL("#{researchUrl}")) + ) + .pause(Duration.ofMillis(500)) + // Add a comment/note about the research + .exec(session -> { + String commentUrl = "comment:" + java.util.UUID.randomUUID().toString(); + return session.set("commentUrl", commentUrl); + }) + .exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Add Research Note - #{topic}") + .post("/api/v1/ref") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .body(StringBody(""" + { + "url": "#{commentUrl}", + "title": "Research Notes: #{topic}", + "comment": "Key insights and takeaways from #{topic} research session", + "tags": ["note", "#{categoryTag}", "research.summary"], + "sources": ["#{researchUrl}"] + }""")) + .check(status().is(201)) + ) + .pause(Duration.ofSeconds(1, 3)) + // Check if there are related templates + .exec( + http("Find Related Templates - #{category}") + .get("/api/v1/template/page") + .queryParam("query", "#{categoryTag}") + .queryParam("size", "10") + .check(status().is(200)) + ); + + // ====================== Daily Review Journey ====================== + + ChainBuilder dailyReviewWorkflow = exec(session -> { + System.out.println("User performing daily review"); + return session; + }) + // Check recent activity + .exec( + http("Check Recent Updates") + .get("/api/v1/ref/page") + .queryParam("size", "30") + .queryParam("sort", "modified,desc") + .check(status().is(200)) + .check(jsonPath("$.content[*].url").findAll().saveAs("recentUrls")) + ) + .pause(Duration.ofSeconds(2, 4)) + // Create a ref to review if researchUrl not already set + .doIf(session -> !session.contains("researchUrl")).then( + feed(topicFeeder) + .exec(session -> { + String url = "https://dailyreview.example.com/item-" + System.currentTimeMillis(); + String topicTag = session.getString("topic").toLowerCase().replaceAll("\\s+", "."); + return session.set("researchUrl", url) + .set("topicTag", topicTag); + }) + .exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Create Review Item") + .post("/api/v1/ref") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .body(StringBody(""" + { + "url": "#{researchUrl}", + "title": "Daily Review Item #{randomInt(1,1000)}", + "comment": "Item for daily review", + "tags": ["review", "#{topicTag}"] + }""")) + .check(status().in(201, 409)) + ) + .pause(Duration.ofMillis(200)) + ) + // Review specific items + .exec( + http("Review Specific Item") + .get("/api/v1/ref") + .queryParam("url", "#{researchUrl}") + .check(status().is(200)) + ) + .pause(Duration.ofSeconds(1, 2)) + // Look for untagged content to organize + .exec( + http("Find Untagged Content") + .get("/api/v1/ref/page") + .queryParam("untagged", "true") + .queryParam("size", "10") + .check(status().is(200)) + ) + .pause(Duration.ofSeconds(2, 4)) + // Fetch a ref to update (to get its cursor/modified timestamp) + .exec( + http("Fetch Ref for Update") + .get("/api/v1/ref") + .queryParam("url", "#{researchUrl}") + .check(status().is(200)) + .check(jsonPath("$.modified").optional().saveAs("refModified")) + ) + .pause(Duration.ofMillis(500)) + // Update a reference with additional tags (using merge-patch) - only if we got the modified timestamp + .doIf(session -> session.contains("refModified")).then( + exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Update Reference Tags") + .patch("/api/v1/ref") + .queryParam("url", "#{researchUrl}") + .queryParam("cursor", "#{refModified}") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .header("Content-Type", "application/merge-patch+json") + .body(StringBody(""" + { + "tags": ["organized", "daily.review", "review#{randomInt(10000,99999)}"] + }""")) + .check(status().is(200)) + ) + ); + + // ====================== Content Curation Journey ====================== + + ChainBuilder curationWorkflow = feed(topicFeeder) + .exec(session -> { + System.out.println("User curating content for: " + session.getString("topic")); + // Convert topic to lowercase and replace spaces with dots for valid tag + String topic = session.getString("topic"); + String topicTag = topic.toLowerCase().replaceAll("\\s+", "."); + String categoryTag = session.getString("category").toLowerCase(); + int randomId = new java.util.Random().nextInt(100) + 1; + return session.set("topicTag", topicTag) + .set("categoryTag", categoryTag) + .set("templateTagValue", "_template/" + topicTag + "." + randomId); + }) + // Create a specialized template for the topic + .exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Create Topic Template - #{topic}") + .post("/api/v1/template") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .body(StringBody(""" + { + "tag": "#{templateTagValue}", + "name": "#{topic} Resource Template", + "config": { + "description": "Template for organizing #{topic} resources", + "category": "#{category}" + }, + "defaults": { + "difficulty": "beginner", + "resource_type": "tutorial", + "quality_rating": 5 + }, + "schema": { + "optionalProperties": { + "difficulty": {"enum": ["beginner", "intermediate", "advanced"]}, + "resource_type": {"enum": ["tutorial", "reference", "example", "tool"]}, + "quality_rating": {"type": "float64"} + } + } + }""")) + .check(status().in(201, 409)) // 201 Created or 409 Conflict if already exists + ) + .pause(Duration.ofSeconds(1, 2)) + // Create a plugin for enhanced functionality + .exec(session -> { + String topicTag = session.getString("topicTag"); + int randomId = new java.util.Random().nextInt(100) + 1; + return session.set("pluginTagValue", "+plugin/" + topicTag + ".enhancer." + randomId); + }) + .exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Create Enhancement Plugin - #{topic}") + .post("/api/v1/plugin") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .body(StringBody(""" + { + "tag": "#{pluginTagValue}", + "name": "#{topic} Content Enhancer", + "config": { + "type": "enhancer", + "description": "Enhances #{topic} content with additional metadata", + "selector": ".#{category}-content", + "features": ["auto-tagging", "difficulty-detection", "related-content"] + } + }""")) + .check(status().in(201, 409)) // 201 Created or 409 Conflict if already exists + ) + .pause(Duration.ofSeconds(1, 3)) + // Check existing extensions for the topic + .exec( + http("Browse Topic Extensions - #{category}") + .get("/api/v1/ext/page") + .queryParam("query", "#{categoryTag}") + .queryParam("size", "15") + .check(status().is(200)) + ) + .pause(Duration.ofSeconds(1, 2)) + // Create a template for curated collections + .exec(session -> { + String topicTag = session.getString("topicTag"); + int randomId = new java.util.Random().nextInt(50) + 1; + return session.set("collectionTag", "collection/" + topicTag + "." + randomId) + .set("collectionTemplateTag", "collection"); + }) + .exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Create Collection Template") + .post("/api/v1/template") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .body(StringBody(""" + { + "tag": "#{collectionTemplateTag}", + "name": "Collection Template", + "config": { + "description": "Template for curated collections" + }, + "defaults": { + "type": "collection", + "curator": "system", + "criteria": { + "quality_min": 7, + "verified": true + } + }, + "schema": { + "optionalProperties": { + "type": {"type": "string"}, + "description": {"type": "string"}, + "curator": {"type": "string"}, + "created": {"type": "string"}, + "criteria": { + "optionalProperties": { + "quality_min": {"type": "float64"}, + "categories": {"elements": {"type": "string"}}, + "verified": {"type": "boolean"} + } + } + } + } + }""")) + .check(status().in(201, 409)) + ) + .pause(Duration.ofMillis(500)) + // Create a curated collection + .exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Create Curated Collection - #{topic}") + .post("/api/v1/ext") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .body(StringBody(""" + { + "tag": "#{collectionTag}", + "name": "#{topic} Curated Collection", + "config": { + "type": "collection", + "description": "Carefully curated resources for learning #{topic}", + "curator": "system", + "created": "#{randomLong()}", + "criteria": { + "quality_min": 7, + "categories": ["#{category}"], + "verified": true + } + } + }""")) + .check(status().in(201, 409)) // 201 Created or 409 Conflict if already exists + ); + + // ====================== Collaborative Work Journey ====================== + + ChainBuilder collaborationWorkflow = exec(session -> { + System.out.println("User engaging in collaborative work"); + return session; + }) + // Check user information and permissions + .exec( + http("Check User Profile") + .get("/api/v1/user/whoami") + .check(status().is(200)) + ) + .pause(Duration.ofSeconds(1)) + // Look for shared content + .exec( + http("Browse Shared Content") + .get("/api/v1/ref/page") + .queryParam("query", "shared") + .queryParam("size", "20") + .check(status().is(200)) + ) + .pause(Duration.ofSeconds(1, 3)) + // Add a collaborative comment + .exec(session -> { + String commentUrl = "comment:collaboration-" + java.util.UUID.randomUUID().toString(); + return session.set("collabCommentUrl", commentUrl); + }) + .exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Add Collaborative Comment") + .post("/api/v1/ref") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .body(StringBody(""" + { + "url": "#{collabCommentUrl}", + "title": "Team Discussion Point", + "comment": "Adding my thoughts on this topic for team review", + "tags": ["collaboration", "team.input", "discussion"], + "sources": ["https://example.com/shared-doc-#{randomInt(1,20)}"] + }""")) + .check(status().is(201)) + ) + .pause(Duration.ofSeconds(1, 2)) + // Check graph relationships for collaborative content + .exec( + http("Check Collaboration Graph") + .get("/api/v1/graph/list") + .multivaluedQueryParam("urls", java.util.Arrays.asList( + "https://example.com/shared-doc-1", + "https://example.com/shared-doc-2", + "comment:collaboration-session1000" + )) + .check(status().is(200)) + ) + .pause(Duration.ofSeconds(1, 3)) + // Create a shared template + .exec(session -> { + String templateTag = "_template/team.standard." + System.currentTimeMillis(); + return session.set("teamTemplateTag", templateTag); + }) + .exec(addCookie(Cookie("XSRF-TOKEN", STATIC_XSRF_TOKEN).withDomain("localhost").withPath("/").withSecure(false))) + .exec( + http("Create Shared Template") + .post("/api/v1/template") + .header("X-XSRF-TOKEN", STATIC_XSRF_TOKEN) + .body(StringBody(""" + { + "tag": "#{teamTemplateTag}", + "name": "Team Standard Template", + "config": { + "description": "Standardized template for team collaboration", + "shared": true, + "team": "development" + }, + "defaults": { + "assignee": "", + "priority": "medium", + "status": "draft" + }, + "schema": { + "properties": { + "assignee": {"type": "string"} + }, + "optionalProperties": { + "priority": {"enum": ["low", "medium", "high", "urgent"]}, + "status": {"enum": ["draft", "review", "approved", "archived"]} + } + } + }""")) + .check(status().in(201, 409)) // 201 Created or 409 Conflict if already exists + ); + + // ====================== Scenarios ====================== + + ScenarioBuilder researchSession = scenario("Research Session") + .group("Research Session").on( + exec(researchWorkflow) + .pause(Duration.ofSeconds(5, 10)) + .repeat(2).on( + exec(researchWorkflow).pause(Duration.ofSeconds(10, 20)) + ) + ); + + ScenarioBuilder dailyReview = scenario("Daily Review") + .group("Daily Review").on( + exec(dailyReviewWorkflow) + .pause(Duration.ofSeconds(3, 8)) + ); + + ScenarioBuilder contentCuration = scenario("Content Curation") + .exec(curationWorkflow) + .pause(Duration.ofSeconds(8, 15)); + + ScenarioBuilder collaborativeWork = scenario("Collaborative Work") + .exec(collaborationWorkflow) + .pause(Duration.ofSeconds(5, 12)); + + // Mixed realistic user behavior + ScenarioBuilder realisticUser = scenario("Realistic User Behavior") + .randomSwitch().on( + percent(40.0).then(exec(dailyReviewWorkflow)), + percent(30.0).then(exec(researchWorkflow)), + percent(20.0).then(exec(collaborationWorkflow)), + percent(10.0).then(exec(curationWorkflow)) + ) + .pause(Duration.ofSeconds(10, 30)); + + { + setUp( + // Research sessions - intensive but focused + researchSession.injectOpen( + nothingFor(Duration.ofSeconds(5)), + rampUsers(8).during(Duration.ofSeconds(30)), + constantUsersPerSec(3).during(Duration.ofMinutes(2)) + ), + // Daily reviews - regular, moderate activity + dailyReview.injectOpen( + nothingFor(Duration.ofSeconds(2)), + rampUsers(15).during(Duration.ofSeconds(20)), + constantUsersPerSec(7).during(Duration.ofMinutes(3)) + ), + // Content curation - specialized, lower frequency + contentCuration.injectOpen( + nothingFor(Duration.ofSeconds(10)), + rampUsers(4).during(Duration.ofSeconds(25)), + constantUsersPerSec(1).during(Duration.ofMinutes(2)) + ), + // Collaborative work - sporadic but important + collaborativeWork.injectOpen( + nothingFor(Duration.ofSeconds(8)), + rampUsers(6).during(Duration.ofSeconds(20)), + constantUsersPerSec(2).during(Duration.ofMinutes(2)) + ), + // Realistic mixed behavior - most common pattern + realisticUser.injectOpen( + nothingFor(Duration.ofSeconds(1)), + rampUsers(25).during(Duration.ofSeconds(45)), + constantUsersPerSec(12).during(Duration.ofMinutes(4)) + ) + ).protocols(httpProtocol) + .maxDuration(Duration.ofMinutes(5)) + .assertions( + global().responseTime().max().lt(8000), + global().responseTime().mean().lt(2000), + global().successfulRequests().percent().gt(75.0), + details("Research Session").responseTime().percentile3().lt(4000), + details("Daily Review").responseTime().percentile3().lt(3000) + ); + } +} diff --git a/jdk_25_maven/cs/rest/jasper/gatling/src/test/resources/data/sources.csv b/jdk_25_maven/cs/rest/jasper/gatling/src/test/resources/data/sources.csv new file mode 100644 index 000000000..4580d1f15 --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/gatling/src/test/resources/data/sources.csv @@ -0,0 +1,9 @@ +source,type +arxiv.org,paper +github.com,code +medium.com,article +stackoverflow.com,qa +docs.spring.io,documentation +kubernetes.io,official-docs +martinfowler.com,blog +youtube.com,video diff --git a/jdk_25_maven/cs/rest/jasper/gatling/src/test/resources/data/topics.csv b/jdk_25_maven/cs/rest/jasper/gatling/src/test/resources/data/topics.csv new file mode 100644 index 000000000..609b04dcc --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/gatling/src/test/resources/data/topics.csv @@ -0,0 +1,9 @@ +topic,category +Machine Learning,AI +Microservices,Architecture +Data Science,Analytics +Kubernetes,DevOps +React Development,Frontend +Database Design,Backend +Security Best Practices,Security +API Design,Architecture diff --git a/jdk_25_maven/cs/rest/jasper/mvnw b/jdk_25_maven/cs/rest/jasper/mvnw new file mode 100644 index 000000000..bd8896bf2 --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/mvnw @@ -0,0 +1,295 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.4 +# +# Optional ENV vars +# ----------------- +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output +# ---------------------------------------------------------------------------- + +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x + +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac + +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + if [ -n "${JAVA_HOME-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi + fi + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi + fi +} + +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" + done + printf %x\\n $h +} + +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } + +die() { + printf %s\\n "$1" >&2 + exit 1 +} + +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + +scriptDir="$(dirname "$0")" +scriptName="$(basename "$0")" + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} + +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" +fi + +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" +fi + +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" +fi + +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi + +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +actualDistributionDir="" + +# First try the expected directory name (for regular distributions) +if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then + if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then + actualDistributionDir="$distributionUrlNameMain" + fi +fi + +# If not found, search for any directory with the Maven executable (for snapshots) +if [ -z "$actualDistributionDir" ]; then + # enable globbing to iterate over items + set +f + for dir in "$TMP_DOWNLOAD_DIR"/*; do + if [ -d "$dir" ]; then + if [ -f "$dir/bin/$MVN_CMD" ]; then + actualDistributionDir="$(basename "$dir")" + break + fi + fi + done + set -f +fi + +if [ -z "$actualDistributionDir" ]; then + verbose "Contents of $TMP_DOWNLOAD_DIR:" + verbose "$(ls -la "$TMP_DOWNLOAD_DIR")" + die "Could not find Maven distribution directory in extracted archive" +fi + +verbose "Found extracted Maven distribution directory: $actualDistributionDir" +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/jdk_25_maven/cs/rest/jasper/mvnw.cmd b/jdk_25_maven/cs/rest/jasper/mvnw.cmd new file mode 100644 index 000000000..92450f932 --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/mvnw.cmd @@ -0,0 +1,189 @@ +<# : batch portion +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.3.4 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' + +$MAVEN_M2_PATH = "$HOME/.m2" +if ($env:MAVEN_USER_HOME) { + $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME" +} + +if (-not (Test-Path -Path $MAVEN_M2_PATH)) { + New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null +} + +$MAVEN_WRAPPER_DISTS = $null +if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) { + $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists" +} else { + $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists" +} + +$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain" +$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +$actualDistributionDir = "" + +# First try the expected directory name (for regular distributions) +$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain" +$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD" +if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) { + $actualDistributionDir = $distributionUrlNameMain +} + +# If not found, search for any directory with the Maven executable (for snapshots) +if (!$actualDistributionDir) { + Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object { + $testPath = Join-Path $_.FullName "bin/$MVN_CMD" + if (Test-Path -Path $testPath -PathType Leaf) { + $actualDistributionDir = $_.Name + } + } +} + +if (!$actualDistributionDir) { + Write-Error "Could not find Maven distribution directory in extracted archive" +} + +Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir" +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/jdk_25_maven/cs/rest/jasper/pom.xml b/jdk_25_maven/cs/rest/jasper/pom.xml new file mode 100644 index 000000000..7ccc98cf6 --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/pom.xml @@ -0,0 +1,531 @@ + + + 4.0.0 + jasper + jasper + 1.3.0-SNAPSHOT + jasper + Knowledge graph server + + + 25 + ${java.version} + ${java.version} + ${java.version} + UTF-8 + UTF-8 + jasper.JasperApplication + -Djava.security.egd=file:/dev/./urandom -Xmx256m + @ + 4.1.0 + 2025.1.2 + 1.18.46 + 2.0.5 + 1.6.3 + 3.53.2.1 + 7.4.1.Final + 3.15.4 + 0.2.2 + 1.13 + 0.29.1 + 3.1.0 + 5.4.0 + 1.5.0 + 1.2 + 2.1.0 + 5.6.4 + 2.22.0 + 4.6.0 + 3.20.0 + 1.28.0 + 1.23.1 + 2.3.8 + 2.13.0 + + + + + org.projectlombok + lombok + ${lombok.version} + provided + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-oauth2-resource-server + + + org.springframework.boot + spring-boot-starter-webmvc + + + org.springframework.boot + spring-boot-starter-cache + + + com.github.ben-manes.caffeine + caffeine + + + org.springframework.boot + spring-boot-starter-liquibase + + + jakarta.annotation + jakarta.annotation-api + + + org.springframework.boot + spring-boot-jackson2 + + + com.fasterxml.jackson.module + jackson-module-jaxb-annotations + + + com.fasterxml.jackson.datatype + jackson-datatype-hibernate7 + + + com.fasterxml.jackson.datatype + jackson-datatype-hppc + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + ${springdoc-openapi.version} + + + org.springframework.data + spring-data-commons + + + com.zaxxer + HikariCP + + + org.apache.commons + commons-compress + ${commons-compress.version} + + + org.apache.commons + commons-collections4 + ${commons-collections4.version} + + + org.apache.commons + commons-lang3 + ${commons-lang3.version} + + + commons-io + commons-io + ${commons-io.version} + + + com.drewnoakes + metadata-extractor + 2.21.0 + + + com.jsontypedef.jtd + jtd + ${jsontypedef.version} + + + com.github.java-json-tools + json-patch + ${json-patch.version} + + + org.hibernate.orm + hibernate-jpamodelgen + ${hibernate-jpamodelgen.version} + provided + + + org.hibernate.orm + hibernate-processor + provided + + + org.hibernate.orm + hibernate-core + + + org.hibernate.validator + hibernate-validator + + + io.hypersistence + hypersistence-utils-hibernate-71 + ${hypersistence-utils-71.version} + + + org.mapstruct + mapstruct + ${mapstruct.version} + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + provided + + + org.springframework.boot + spring-boot-configuration-processor + provided + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-logging + + + org.zalando + problem-spring-web + ${problem-spring-web.version} + + + org.springframework.boot + spring-boot-starter-tomcat + + + org.springframework.boot + spring-boot-starter-mail + + + org.springframework + spring-websocket + + + org.springframework + spring-messaging + + + org.springframework.cloud + spring-cloud-starter-kubernetes-fabric8 + + + com.rometools + rome + ${rome.version} + + + com.rometools + rome-modules + ${rome.version} + + + org.apache.httpcomponents.client5 + httpclient5 + ${apache-httpclient.version} + + + org.jsoup + jsoup + ${jsoup.version} + + + com.unboundid.product.scim2 + scim2-sdk-client + ${scim2-client.version} + + + io.github.openfeign + feign-httpclient + + + org.apache.sshd + sshd-core + 2.19.0 + + + org.postgresql + postgresql + runtime + + + org.xerial + sqlite-jdbc + ${sqlite-jdbc.version} + + + org.hibernate.orm + hibernate-community-dialects + + + com.rubiconproject.oss + jchronic + 0.2.8 + + + + + org.springframework.cloud + spring-cloud-starter-circuitbreaker-resilience4j + + + io.github.resilience4j + resilience4j-bulkhead + + + org.springframework.retry + spring-retry + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + org.springframework.security + spring-security-data + + + org.springframework.boot + spring-boot-starter-integration + + + org.springframework.integration + spring-integration-redis + + + org.springframework.boot + spring-boot-starter-data-redis + + + io.micrometer + micrometer-registry-prometheus + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-webmvc-test + test + + + org.xmlunit + xmlunit-core + ${xmlunit.version} + test + + + + javax.xml.bind + jaxb-api + + + + + org.springframework.security + spring-security-test + test + + + org.springframework.integration + spring-integration-test + test + + + org.testcontainers + testcontainers-junit-jupiter + test + + + org.testcontainers + testcontainers-postgresql + test + + + org.springframework.boot + spring-boot-starter-liquibase-test + test + + + com.tngtech.archunit + archunit-junit5-api + ${archunit-junit5.version} + test + + + + + com.tngtech.archunit + archunit-junit5-engine + ${archunit-junit5.version} + test + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + org.testcontainers + testcontainers-bom + ${testcontainers.version} + pom + import + + + + + + spring-boot:run + + + ${basedir}/src/main/resources + true + + **/application*.yml + **/application*.yaml + **/application*.properties + + + + ${basedir}/src/main/resources + + **/application*.yml + **/application*.yaml + **/application*.properties + + + + + + org.jacoco + jacoco-maven-plugin + 0.8.15 + + + + prepare-agent + + + + report + + report + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + ${start-class} + + true + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.15.0 + + true + ${java.version} + ${java.version} + ${java.version} + full + + + + org.apache.maven.plugins + maven-resources-plugin + 3.5.0 + + ${project.build.sourceEncoding} + + ${resource.delimiter} + + false + + + + org.apache.maven.plugins + maven-jar-plugin + 3.5.1 + + + + ${start-class} + true + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.5.6 + + + **/*Test.java + **/*IT.java + + @{argLine} -Xmx1024m + + + + org.apache.maven.plugins + maven-failsafe-plugin + 3.5.6 + + + + integration-test + verify + + + + + ${project.build.outputDirectory} + + + + + diff --git a/jdk_25_maven/cs/rest/jasper/src/main/java/jasper/JasperApplication.java b/jdk_25_maven/cs/rest/jasper/src/main/java/jasper/JasperApplication.java new file mode 100644 index 000000000..1d6e0f910 --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/src/main/java/jasper/JasperApplication.java @@ -0,0 +1,29 @@ +package jasper; + +import jasper.config.Props; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.data.redis.autoconfigure.DataRedisAutoConfiguration; +import org.springframework.boot.liquibase.autoconfigure.LiquibaseProperties; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8CatalogWatchAutoConfiguration; +import org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8DiscoveryClientAutoConfiguration; + +@SpringBootApplication( + exclude = { + Fabric8DiscoveryClientAutoConfiguration.class, + Fabric8CatalogWatchAutoConfiguration.class, + DataRedisAutoConfiguration.class, + }, + excludeName = "org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8InformerAutoConfiguration" +) +@EnableConfigurationProperties({ LiquibaseProperties.class, Props.class }) +@EnableCaching +public class JasperApplication { + + static void main(String[] args) { + SpringApplication.run(JasperApplication.class, args); + } + +} diff --git a/jdk_25_maven/cs/rest/jasper/src/main/java/jasper/aop/ClearIdle.java b/jdk_25_maven/cs/rest/jasper/src/main/java/jasper/aop/ClearIdle.java new file mode 100644 index 000000000..ef3debb54 --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/src/main/java/jasper/aop/ClearIdle.java @@ -0,0 +1,16 @@ +package jasper.aop; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to mark REST controllers or methods that should reset the idle timer. + * Used by the backfill system to detect when the server is idle. + * Should not be applied to public replication endpoints (pub/). + */ +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ClearIdle { +} diff --git a/jdk_25_maven/cs/rest/jasper/src/main/java/jasper/aop/IdleAspect.java b/jdk_25_maven/cs/rest/jasper/src/main/java/jasper/aop/IdleAspect.java new file mode 100644 index 000000000..1571654da --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/src/main/java/jasper/aop/IdleAspect.java @@ -0,0 +1,56 @@ +package jasper.aop; + +import jasper.config.Props; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.time.Instant; + +/** + * Aspect that resets the idle timer when a REST controller method annotated + * with @ClearIdle is invoked. + */ +@Aspect +@Component +public class IdleAspect { + + @Autowired + Props props; + + private volatile Instant lastActivity = Instant.now(); + + /** + * Record that the server received a REST API request. + */ + public void clearIdle() { + lastActivity = Instant.now(); + } + + /** + * Check if the server has been idle for the configured amount of time. + */ + public boolean isIdle() { + if (props.getBackfillIdleSec() <= 0) return true; + return Instant.now().isAfter(lastActivity.plusSeconds(props.getBackfillIdleSec())); + } + + /** + * Pointcut matching methods in classes annotated with @ClearIdle. + */ + @Pointcut("within(@jasper.aop.ClearIdle *)") + public void clearIdleClass() {} + + /** + * Pointcut matching methods annotated with @ClearIdle. + */ + @Pointcut("@annotation(jasper.aop.ClearIdle)") + public void clearIdleMethod() {} + + @Before("clearIdleClass() || clearIdleMethod()") + public void resetIdleTimer() { + clearIdle(); + } +} diff --git a/jdk_25_maven/cs/rest/jasper/src/main/java/jasper/aop/logging/LoggingAspect.java b/jdk_25_maven/cs/rest/jasper/src/main/java/jasper/aop/logging/LoggingAspect.java new file mode 100644 index 000000000..08ebd90c4 --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/src/main/java/jasper/aop/logging/LoggingAspect.java @@ -0,0 +1,102 @@ +package jasper.aop.logging; + +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; + +import java.util.Arrays; + +/** + * Aspect for logging execution of service and repository Spring components. + * + * By default, it only runs with the "dev" profile. + */ +@Aspect +public class LoggingAspect { + + private final Environment env; + + public LoggingAspect(Environment env) { + this.env = env; + } + + /** + * Pointcut that matches all repositories, services and Web REST endpoints. + */ + @Pointcut( + "within(@org.springframework.stereotype.Repository *) || " + + "within(@org.springframework.stereotype.Service *) || " + + "within(@org.springframework.web.bind.annotation.RestController *)") + public void springBeanPointcut() { + // Method is empty as this is just a Pointcut, the implementations are in the advices. + } + + /** + * Pointcut that matches all Spring beans in the application's main packages. + */ + @Pointcut( + "within(jasper.client..*) || " + + "within(jasper.repository..*) || " + + "within(jasper.service..*) || " + + "within(jasper.web.rest..*)") + public void applicationPackagePointcut() { + // Method is empty as this is just a Pointcut, the implementations are in the advices. + } + + /** + * Retrieves the {@link Logger} associated to the given {@link JoinPoint}. + * + * @param joinPoint join point we want the logger for. + * @return {@link Logger} associated to the given {@link JoinPoint}. + */ + private Logger logger(JoinPoint joinPoint) { + return LoggerFactory.getLogger(joinPoint.getSignature().getDeclaringTypeName()); + } + + /** + * Advice that logs methods throwing exceptions. + * + * @param joinPoint join point for advice. + * @param e exception. + */ + @AfterThrowing(pointcut = "applicationPackagePointcut() && springBeanPointcut()", throwing = "e") + public void logAfterThrowing(JoinPoint joinPoint, Throwable e) { + logger(joinPoint).debug( + "Exception in {}() with cause = {}", + joinPoint.getSignature().getName(), + e.getCause() != null ? e.getCause() : "NULL" + ); + logger(joinPoint).trace("", e); + } + + /** + * Advice that logs when a method is entered and exited. + * + * @param joinPoint join point for advice. + * @return result. + * @throws Throwable throws {@link IllegalArgumentException}. + */ + @Around("applicationPackagePointcut() && springBeanPointcut()") + public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { + Logger log = logger(joinPoint); + if (log.isDebugEnabled()) { + log.debug("Enter: {}() with argument[s] = {}", joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs())); + } + try { + Object result = joinPoint.proceed(); + if (log.isDebugEnabled()) { + log.debug("Exit: {}() with result = {}", joinPoint.getSignature().getName(), result); + } + return result; + } catch (IllegalArgumentException e) { + log.error("Illegal argument: {} in {}()", Arrays.toString(joinPoint.getArgs()), joinPoint.getSignature().getName()); + throw e; + } + } +} diff --git a/jdk_25_maven/cs/rest/jasper/src/main/java/jasper/client/JasperClient.java b/jdk_25_maven/cs/rest/jasper/src/main/java/jasper/client/JasperClient.java new file mode 100644 index 000000000..98c4d047a --- /dev/null +++ b/jdk_25_maven/cs/rest/jasper/src/main/java/jasper/client/JasperClient.java @@ -0,0 +1,135 @@ +package jasper.client; + +import feign.HeaderMap; +import feign.Param; +import feign.QueryMap; +import feign.RequestLine; +import jasper.domain.Ext; +import jasper.domain.Plugin; +import jasper.domain.Ref; +import jasper.domain.Template; +import jasper.domain.User; +import jasper.service.dto.RefReplDto; +import jasper.service.dto.UserDto; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.core.io.Resource; +import org.springframework.http.ResponseEntity; +import org.springframework.web.context.request.WebRequest; + +import java.net.URI; +import java.time.Instant; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static jasper.security.Auth.LOCAL_ORIGIN_HEADER; +import static jasper.security.Auth.READ_ACCESS_HEADER; +import static jasper.security.Auth.TAG_READ_ACCESS_HEADER; +import static jasper.security.Auth.TAG_WRITE_ACCESS_HEADER; +import static jasper.security.Auth.USER_ROLE_HEADER; +import static jasper.security.Auth.USER_TAG_HEADER; +import static jasper.security.Auth.WRITE_ACCESS_HEADER; + +@FeignClient(value = "jasper", url = "https://jasperkm.info") +public interface JasperClient { + + @RequestLine("GET /pub/api/v1/repl/ref") + List refPull(URI baseUri, @QueryMap Map params); + @RequestLine("GET /pub/api/v1/repl/ref") + List refPull(URI baseUri, @HeaderMap Map headers, @QueryMap Map params); + @RequestLine("GET /pub/api/v1/repl/ref/cursor?origin={origin}") + Instant refCursor(URI baseUri, @Param("origin") String origin); + @RequestLine("GET /pub/api/v1/repl/ref/cursor?origin={origin}") + Instant refCursor(URI baseUri, @HeaderMap Map headers, @Param("origin") String origin); + @RequestLine("POST /pub/api/v1/repl/ref?origin={origin}") + void refPush(URI baseUri, @Param("origin") String origin, List push); + @RequestLine("POST /pub/api/v1/repl/ref?origin={origin}") + void refPush(URI baseUri, @HeaderMap Map headers, @Param("origin") String origin, List push); + + @RequestLine("GET /pub/api/v1/repl/ext") + List extPull(URI baseUri, @QueryMap Map params); + @RequestLine("GET /pub/api/v1/repl/ext") + List extPull(URI baseUri, @HeaderMap Map headers, @QueryMap Map params); + @RequestLine("GET /pub/api/v1/repl/ext/cursor?origin={origin}") + Instant extCursor(URI baseUri, @Param("origin") String origin); + @RequestLine("GET /pub/api/v1/repl/ext/cursor?origin={origin}") + Instant extCursor(URI baseUri, @HeaderMap Map headers, @Param("origin") String origin); + @RequestLine("POST /pub/api/v1/repl/ext?origin={origin}") + void extPush(URI baseUri, @Param("origin") String origin, List push); + @RequestLine("POST /pub/api/v1/repl/ext?origin={origin}") + void extPush(URI baseUri, @HeaderMap Map headers, @Param("origin") String origin, List push); + + @RequestLine("GET /pub/api/v1/repl/user") + List userPull(URI baseUri, @QueryMap Map params); + @RequestLine("GET /pub/api/v1/repl/user") + List userPull(URI baseUri, @HeaderMap Map headers, @QueryMap Map params); + @RequestLine("GET /pub/api/v1/repl/user/cursor?origin={origin}") + Instant userCursor(URI baseUri, @Param("origin") String origin); + @RequestLine("GET /pub/api/v1/repl/user/cursor?origin={origin}") + Instant userCursor(URI baseUri, @HeaderMap Map headers, @Param("origin") String origin); + @RequestLine("POST /pub/api/v1/repl/user?origin={origin}") + void userPush(URI baseUri, @Param("origin") String origin, List push); + @RequestLine("POST /pub/api/v1/repl/user?origin={origin}") + void userPush(URI baseUri, @HeaderMap Map headers, @Param("origin") String origin, List push); + + @RequestLine("GET /pub/api/v1/repl/plugin") + List pluginPull(URI baseUri, @QueryMap Map params); + @RequestLine("GET /pub/api/v1/repl/plugin") + List pluginPull(URI baseUri, @HeaderMap Map headers, @QueryMap Map params); + @RequestLine("GET /pub/api/v1/repl/plugin/cursor?origin={origin}") + Instant pluginCursor(URI baseUri, @Param("origin") String origin); + @RequestLine("GET /pub/api/v1/repl/plugin/cursor?origin={origin}") + Instant pluginCursor(URI baseUri, @HeaderMap Map headers, @Param("origin") String origin); + @RequestLine("POST /pub/api/v1/repl/plugin?origin={origin}") + void pluginPush(URI baseUri, @Param("origin") String origin, List push); + @RequestLine("POST /pub/api/v1/repl/plugin?origin={origin}") + void pluginPush(URI baseUri, @HeaderMap Map headers, @Param("origin") String origin, List push); + + @RequestLine("GET /pub/api/v1/repl/template") + List