From 8f7a18a32504e4d665ed97dd9061e4809b37c711 Mon Sep 17 00:00:00 2001 From: Vladimir Orany Date: Fri, 2 Sep 2022 16:17:17 +0200 Subject: [PATCH 01/51] Log context initialization errors (#1434) --- .../function/aws/MicronautRequestHandler.java | 26 ++++++++++++++++--- .../aws/MicronautRequestStreamHandler.java | 17 +++++++++--- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/function-aws/src/main/java/io/micronaut/function/aws/MicronautRequestHandler.java b/function-aws/src/main/java/io/micronaut/function/aws/MicronautRequestHandler.java index 871a3317fd..3301a43e65 100644 --- a/function-aws/src/main/java/io/micronaut/function/aws/MicronautRequestHandler.java +++ b/function-aws/src/main/java/io/micronaut/function/aws/MicronautRequestHandler.java @@ -25,6 +25,8 @@ import io.micronaut.core.reflect.GenericTypeUtils; import io.micronaut.core.util.ArrayUtils; import io.micronaut.function.executor.AbstractFunctionExecutor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.slf4j.MDC; import java.util.Optional; @@ -85,6 +87,11 @@ public abstract class MicronautRequestHandler extends AbstractFunctionExec @Deprecated public static final String MDC_DEFAULT_XRAY_TRACE_ID = "AWS-XRAY-TRACE-ID"; + /** + * Logger for the application context creation errors. + */ + private static final Logger LOG = LoggerFactory.getLogger(MicronautRequestHandler.class); + @SuppressWarnings("unchecked") private final Class inputType = initTypeArgument(); @@ -93,8 +100,13 @@ public abstract class MicronautRequestHandler extends AbstractFunctionExec * Lambda deployment. */ public MicronautRequestHandler() { - buildApplicationContext(null); - injectIntoApplicationContext(); + try { + buildApplicationContext(null); + injectIntoApplicationContext(); + } catch (Exception e) { + LOG.error("Exception initializing handler", e); + throw e; + } } /** @@ -103,8 +115,14 @@ public MicronautRequestHandler() { */ public MicronautRequestHandler(ApplicationContext applicationContext) { this.applicationContext = applicationContext; - startEnvironment(applicationContext); - injectIntoApplicationContext(); + + try { + startEnvironment(applicationContext); + injectIntoApplicationContext(); + } catch (Exception e) { + LOG.error("Exception initializing handler: " + e.getMessage() , e); + throw e; + } } /** diff --git a/function-aws/src/main/java/io/micronaut/function/aws/MicronautRequestStreamHandler.java b/function-aws/src/main/java/io/micronaut/function/aws/MicronautRequestStreamHandler.java index ea44a6c93f..116567a39c 100644 --- a/function-aws/src/main/java/io/micronaut/function/aws/MicronautRequestStreamHandler.java +++ b/function-aws/src/main/java/io/micronaut/function/aws/MicronautRequestStreamHandler.java @@ -23,12 +23,13 @@ import io.micronaut.core.annotation.Nullable; import io.micronaut.function.executor.StreamFunctionExecutor; import io.micronaut.core.annotation.NonNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import static io.micronaut.function.aws.MicronautRequestHandler.registerContextBeans; - /** *

An implementation of the {@link RequestStreamHandler} for Micronaut

. * @@ -37,6 +38,11 @@ */ public class MicronautRequestStreamHandler extends StreamFunctionExecutor implements RequestStreamHandler, MicronautLambdaContext { + /** + * Logger for the application context creation errors. + */ + private static final Logger LOG = LoggerFactory.getLogger(MicronautRequestStreamHandler.class); + @Nullable private String ctxFunctionName; @@ -48,7 +54,12 @@ public MicronautRequestStreamHandler() { // initialize the application context in the constructor // this is faster in Lambda as init cost is giving higher processor priority // see https://github.com/micronaut-projects/micronaut-aws/issues/18#issuecomment-530903419 - buildApplicationContext(null); + try { + buildApplicationContext(null); + } catch (Exception e) { + LOG.error("Exception initializing handler: " + e.getMessage(), e); + throw e; + } } /** From a3e8670106115eee541368cf6d18ba5cc9e8ad58 Mon Sep 17 00:00:00 2001 From: micronaut-build Date: Fri, 2 Sep 2022 18:17:18 +0000 Subject: [PATCH 02/51] [skip ci] Release v3.8.0 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 6b1aaa2636..77fe92b647 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -projectVersion=3.8.0-SNAPSHOT +projectVersion=3.8.0 projectGroup=io.micronaut.aws micronautDocsVersion=2.0.0 From cb70ef3a87df7527d877892d5cf05c91819cc99b Mon Sep 17 00:00:00 2001 From: micronaut-build Date: Fri, 2 Sep 2022 18:22:25 +0000 Subject: [PATCH 03/51] Back to 3.8.1-SNAPSHOT --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 77fe92b647..e94b1f3cd7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -projectVersion=3.8.0 +projectVersion=3.8.1-SNAPSHOT projectGroup=io.micronaut.aws micronautDocsVersion=2.0.0 From 62443db90c52d7b0f209388a5e4bb0fabefa43e8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Sep 2022 10:39:29 +0200 Subject: [PATCH 04/51] fix(deps): update aws-java-sdk-v2 monorepo to v2.17.267 (#1447) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2ca5d92efc..e80fca11a1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,7 @@ fileupload = '0.0.5' jetty = '9.4.48.v20220622' managed-alexa-ask-sdk = "2.43.7" managed-aws-java-sdk-v1 = '1.12.292' -managed-aws-java-sdk-v2 = '2.17.263' +managed-aws-java-sdk-v2 = '2.17.267' managed-aws-lambda = '1.2.1' managed-aws-lambda-events = '3.11.0' managed-aws-serverless-core = '1.8.2' From 85016052a7b78a267ae24b8fababd51ddc1a3d6e Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Mon, 5 Sep 2022 10:40:50 +0200 Subject: [PATCH 05/51] ci: projectVersion=3.9.0-SNAPSHOT --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index e94b1f3cd7..0d8045cebb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -projectVersion=3.8.1-SNAPSHOT +projectVersion=3.9.0-SNAPSHOT projectGroup=io.micronaut.aws micronautDocsVersion=2.0.0 From 0eb425ccff1de9b9dc896127037f22ac2be9c3c7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Sep 2022 10:41:15 +0200 Subject: [PATCH 06/51] fix(deps): update managed-alexa-ask-sdk to v2.44.0 (#1450) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e80fca11a1..ad39556c92 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ bouncycastle = '1.70' fileupload = '0.0.5' jetty = '9.4.48.v20220622' -managed-alexa-ask-sdk = "2.43.7" +managed-alexa-ask-sdk = "2.44.0" managed-aws-java-sdk-v1 = '1.12.292' managed-aws-java-sdk-v2 = '2.17.267' managed-aws-lambda = '1.2.1' From d5a4cd59a93315423bbc2a9aedd4f5792a589882 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Sep 2022 10:41:24 +0200 Subject: [PATCH 07/51] fix(deps): update dependency software.amazon.awscdk:aws-cdk-lib to v2.40.0 (#1449) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ad39556c92..461a6552bf 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ managed-aws-java-sdk-v2 = '2.17.267' managed-aws-lambda = '1.2.1' managed-aws-lambda-events = '3.11.0' managed-aws-serverless-core = '1.8.2' -managed-aws-cdk-lib='2.39.1' +managed-aws-cdk-lib='2.40.0' servlet-api = "2.5" slf4j = "1.7.36" From 7c1c7118a8e0827a949e2280680735763e07abf6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Sep 2022 14:23:17 +0200 Subject: [PATCH 08/51] fix(deps): update micronautversion to v3.6.2 (#1451) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 0d8045cebb..f555ed0dda 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ projectVersion=3.9.0-SNAPSHOT projectGroup=io.micronaut.aws micronautDocsVersion=2.0.0 -micronautVersion=3.6.1 +micronautVersion=3.6.2 micronautTestVersion=3.5.0 groovyVersion=3.0.10 From 5d253f40793987b11b2c10f3de522b951531f32c Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Mon, 5 Sep 2022 15:24:34 +0200 Subject: [PATCH 09/51] fix: split RuntimeApiTest (#1452) * micronaut to 3.6.2 --- ...ntimeSpec.groovy => RuntimeApiSpec.groovy} | 68 ++++-------------- ...ingHeaderPropagationSysPropertySpec.groovy | 69 +++++++++++++++++++ 2 files changed, 84 insertions(+), 53 deletions(-) rename function-aws-custom-runtime/src/test/groovy/io/micronaut/function/aws/runtime/{MicronautLambdaRuntimeSpec.groovy => RuntimeApiSpec.groovy} (58%) create mode 100644 function-aws-custom-runtime/src/test/groovy/io/micronaut/function/aws/runtime/TracingHeaderPropagationSysPropertySpec.groovy diff --git a/function-aws-custom-runtime/src/test/groovy/io/micronaut/function/aws/runtime/MicronautLambdaRuntimeSpec.groovy b/function-aws-custom-runtime/src/test/groovy/io/micronaut/function/aws/runtime/RuntimeApiSpec.groovy similarity index 58% rename from function-aws-custom-runtime/src/test/groovy/io/micronaut/function/aws/runtime/MicronautLambdaRuntimeSpec.groovy rename to function-aws-custom-runtime/src/test/groovy/io/micronaut/function/aws/runtime/RuntimeApiSpec.groovy index 3237926511..1c94ab0af0 100644 --- a/function-aws-custom-runtime/src/test/groovy/io/micronaut/function/aws/runtime/MicronautLambdaRuntimeSpec.groovy +++ b/function-aws-custom-runtime/src/test/groovy/io/micronaut/function/aws/runtime/RuntimeApiSpec.groovy @@ -1,73 +1,34 @@ -/* - * Copyright 2017-2019 original authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package io.micronaut.function.aws.runtime import com.amazonaws.serverless.proxy.model.ApiGatewayRequestIdentity import com.amazonaws.serverless.proxy.model.AwsProxyRequest import com.amazonaws.serverless.proxy.model.AwsProxyRequestContext import com.amazonaws.serverless.proxy.model.AwsProxyResponse +import com.amazonaws.services.lambda.runtime.Context import io.micronaut.context.ApplicationContext +import io.micronaut.context.BeanProvider +import io.micronaut.context.annotation.Any import io.micronaut.context.annotation.Requires -import io.micronaut.function.aws.MicronautRequestHandler -import io.micronaut.http.HttpHeaders import io.micronaut.http.HttpResponse +import io.micronaut.http.MediaType import io.micronaut.http.annotation.Body import io.micronaut.http.annotation.Controller import io.micronaut.http.annotation.Get import io.micronaut.http.annotation.PathVariable import io.micronaut.http.annotation.Post +import io.micronaut.http.annotation.Produces import io.micronaut.runtime.server.EmbeddedServer import spock.lang.Specification import spock.util.concurrent.PollingConditions -import spock.util.environment.RestoreSystemProperties - -class MicronautLambdaRuntimeSpec extends Specification { - - @RestoreSystemProperties - void "Tracing header propagated as system property"() { - given: - String traceHeader = 'Root=1-5759e988-bd862e3fe1be46a994272793;Sampled=1' - EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer, ['spec.name': 'MicronautLambdaRuntimeSpec']) - String serverUrl = "localhost:$embeddedServer.port" - CustomMicronautLambdaRuntime customMicronautLambdaRuntime = new CustomMicronautLambdaRuntime(serverUrl) - Thread t = new Thread({ -> - customMicronautLambdaRuntime.run([] as String[]) - }) - t.start() - - when: - def httpHeaders = Stub(HttpHeaders) { - get(LambdaRuntimeInvocationResponseHeaders.LAMBDA_RUNTIME_TRACE_ID) >> traceHeader - } - customMicronautLambdaRuntime.propagateTraceId(httpHeaders) - - then: - System.getProperty(MicronautRequestHandler.LAMBDA_TRACE_HEADER_PROP) == traceHeader - - cleanup: - embeddedServer.close() - } +class RuntimeApiSpec extends Specification { void "test runtime API loop"() { given: - EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer, ['spec.name': 'MicronautLambdaRuntimeSpec']) + EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer, ['spec.name': 'RuntimeApiSpec']) String serverUrl = "localhost:$embeddedServer.port" CustomMicronautLambdaRuntime customMicronautLambdaRuntime = new CustomMicronautLambdaRuntime(serverUrl) Thread t = new Thread({ -> - customMicronautLambdaRuntime.run([] as String[]) + customMicronautLambdaRuntime.run([] as String[]) }) t.start() @@ -77,7 +38,7 @@ class MicronautLambdaRuntimeSpec extends Specification { new PollingConditions(timeout: 5).eventually { assert lambadaRuntimeApi.responses assert lambadaRuntimeApi.responses['123456'] - assert lambadaRuntimeApi.responses['123456'].body == 'Hello 123456' + assert lambadaRuntimeApi.responses['123456'].body == "Hello 123456" } cleanup: @@ -103,14 +64,16 @@ class MicronautLambdaRuntimeSpec extends Specification { @Controller("/hello") static class HelloController { + @Any BeanProvider context + @Produces(MediaType.TEXT_PLAIN) @Get("/world") - String index(AwsProxyRequest request) { - return "Hello " + request.getRequestContext().getRequestId() + String index() { + return "Hello " + context.get().awsRequestId } } - @Requires(property = 'spec.name', value = 'MicronautLambdaRuntimeSpec') + @Requires(property = 'spec.name', value = 'RuntimeApiSpec') @Controller("/") static class MockLambadaRuntimeApi { @@ -126,7 +89,7 @@ class MicronautLambdaRuntimeSpec extends Specification { context.setIdentity(new ApiGatewayRequestIdentity()) req.setRequestContext(context) HttpResponse.ok(req) - .header(LambdaRuntimeInvocationResponseHeaders.LAMBDA_RUNTIME_AWS_REQUEST_ID, "123456") + .header(LambdaRuntimeInvocationResponseHeaders.LAMBDA_RUNTIME_AWS_REQUEST_ID, "123456") } @Post("/2018-06-01/runtime/invocation/{requestId}/response") @@ -135,5 +98,4 @@ class MicronautLambdaRuntimeSpec extends Specification { return HttpResponse.accepted() } } - } diff --git a/function-aws-custom-runtime/src/test/groovy/io/micronaut/function/aws/runtime/TracingHeaderPropagationSysPropertySpec.groovy b/function-aws-custom-runtime/src/test/groovy/io/micronaut/function/aws/runtime/TracingHeaderPropagationSysPropertySpec.groovy new file mode 100644 index 0000000000..2ec238d394 --- /dev/null +++ b/function-aws-custom-runtime/src/test/groovy/io/micronaut/function/aws/runtime/TracingHeaderPropagationSysPropertySpec.groovy @@ -0,0 +1,69 @@ +/* + * Copyright 2017-2019 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.function.aws.runtime + + +import io.micronaut.context.ApplicationContext +import io.micronaut.function.aws.MicronautRequestHandler +import io.micronaut.http.HttpHeaders +import io.micronaut.runtime.server.EmbeddedServer +import spock.lang.Specification +import spock.util.environment.RestoreSystemProperties + +class TracingHeaderPropagationSysPropertySpec extends Specification { + + @RestoreSystemProperties + void "Tracing header propagated as system property"() { + given: + String traceHeader = 'Root=1-5759e988-bd862e3fe1be46a994272793;Sampled=1' + EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer, [:]) + String serverUrl = "localhost:$embeddedServer.port" + CustomMicronautLambdaRuntime customMicronautLambdaRuntime = new CustomMicronautLambdaRuntime(serverUrl) + Thread t = new Thread({ -> + customMicronautLambdaRuntime.run([] as String[]) + }) + t.start() + + when: + def httpHeaders = Stub(HttpHeaders) { + get(LambdaRuntimeInvocationResponseHeaders.LAMBDA_RUNTIME_TRACE_ID) >> traceHeader + } + customMicronautLambdaRuntime.propagateTraceId(httpHeaders) + + then: + System.getProperty(MicronautRequestHandler.LAMBDA_TRACE_HEADER_PROP) == traceHeader + + cleanup: + embeddedServer.close() + } + + class CustomMicronautLambdaRuntime extends MicronautLambdaRuntime { + + String serverUrl + + CustomMicronautLambdaRuntime(String serverUrl) { + super() + this.serverUrl = serverUrl + } + + @Override + String getEnv(String name) { + if (name == ReservedRuntimeEnvironmentVariables.AWS_LAMBDA_RUNTIME_API) { + return serverUrl + } + } + } +} From 9d8aed5cf1b300fac2d553fe4cd1714ca3a3b48b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Sep 2022 15:24:45 +0200 Subject: [PATCH 10/51] fix(deps): update aws-java-sdk monorepo to v1.12.296 (#1446) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 461a6552bf..fb74b703dd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ bouncycastle = '1.70' fileupload = '0.0.5' jetty = '9.4.48.v20220622' managed-alexa-ask-sdk = "2.44.0" -managed-aws-java-sdk-v1 = '1.12.292' +managed-aws-java-sdk-v1 = '1.12.296' managed-aws-java-sdk-v2 = '2.17.267' managed-aws-lambda = '1.2.1' managed-aws-lambda-events = '3.11.0' From ff6f1d101ce529028ca3fe3a0abf455924afeaf0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Sep 2022 15:24:56 +0200 Subject: [PATCH 11/51] chore(deps): update mikepenz/action-junit-report action to v3.4.0 (#1439) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/graalvm.yml | 2 +- .github/workflows/gradle.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/graalvm.yml b/.github/workflows/graalvm.yml index dcd343817a..b0b11df674 100644 --- a/.github/workflows/graalvm.yml +++ b/.github/workflows/graalvm.yml @@ -58,7 +58,7 @@ jobs: PREDICTIVE_TEST_SELECTION: "${{ github.event_name == 'pull_request' && 'true' || 'false' }}" - name: Publish Test Report if: always() - uses: mikepenz/action-junit-report@v3.3.3 + uses: mikepenz/action-junit-report@v3.4.0 with: check_name: GraalVM CE CI / Test Report (Java ${{ matrix.java }}) report_paths: '**/build/test-results/test/TEST-*.xml' diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 452e7efc88..44d493a343 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -60,7 +60,7 @@ jobs: PREDICTIVE_TEST_SELECTION: "${{ github.event_name == 'pull_request' && 'true' || 'false' }}" - name: Publish Test Report if: always() - uses: mikepenz/action-junit-report@v3.3.3 + uses: mikepenz/action-junit-report@v3.4.0 with: check_name: Java CI / Test Report (${{ matrix.java }}) report_paths: '**/build/test-results/test/TEST-*.xml' From 51f94ae52010bbf7d5f6029d8ca54e25da215f8d Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Tue, 6 Sep 2022 13:26:32 +0200 Subject: [PATCH 12/51] use Micronaut version catalogue (#1453) * Move all build.gradle scripts to kotlin Co-authored-by: Tim Yates --- aws-alexa-httpserver/build.gradle | 24 ----------- aws-alexa-httpserver/build.gradle.kts | 24 +++++++++++ aws-alexa/build.gradle | 23 ---------- aws-alexa/build.gradle.kts | 23 ++++++++++ aws-bom/build.gradle | 3 -- aws-bom/build.gradle.kts | 3 ++ aws-cdk/{build.gradle => build.gradle.kts} | 4 +- aws-common/build.gradle | 8 ---- aws-common/build.gradle.kts | 8 ++++ aws-distributed-configuration/build.gradle | 9 ---- .../build.gradle.kts | 9 ++++ aws-parameter-store/build.gradle | 14 ------ aws-parameter-store/build.gradle.kts | 12 ++++++ aws-sdk-v1/build.gradle | 14 ------ aws-sdk-v1/build.gradle.kts | 14 ++++++ aws-sdk-v2/build.gradle | 43 ------------------- aws-sdk-v2/build.gradle.kts | 43 +++++++++++++++++++ aws-secretsmanager/build.gradle | 9 ---- aws-secretsmanager/build.gradle.kts | 9 ++++ aws-service-discovery/build.gradle | 13 ------ aws-service-discovery/build.gradle.kts | 11 +++++ build.gradle | 5 --- build.gradle.kts | 5 +++ function-aws-alexa/build.gradle | 23 ---------- function-aws-alexa/build.gradle.kts | 23 ++++++++++ function-aws-api-proxy-test/build.gradle | 10 ----- function-aws-api-proxy-test/build.gradle.kts | 10 +++++ function-aws-api-proxy/build.gradle | 42 ------------------ function-aws-api-proxy/build.gradle.kts | 42 ++++++++++++++++++ function-aws-custom-runtime/build.gradle | 17 -------- function-aws-custom-runtime/build.gradle.kts | 17 ++++++++ function-aws-test/build.gradle | 11 ----- function-aws-test/build.gradle.kts | 11 +++++ .../{build.gradle => build.gradle.kts} | 6 +-- function-client-aws/build.gradle | 17 -------- function-client-aws/build.gradle.kts | 17 ++++++++ gradle/libs.versions.toml | 19 -------- settings.gradle | 34 --------------- settings.gradle.kts | 38 ++++++++++++++++ test-suite-groovy/build.gradle | 28 ------------ test-suite-groovy/build.gradle.kts | 34 +++++++++++++++ test-suite-kotlin/build.gradle | 37 ---------------- test-suite-kotlin/build.gradle.kts | 41 ++++++++++++++++++ test-suite/build.gradle | 33 -------------- test-suite/build.gradle.kts | 37 ++++++++++++++++ 45 files changed, 437 insertions(+), 440 deletions(-) delete mode 100644 aws-alexa-httpserver/build.gradle create mode 100644 aws-alexa-httpserver/build.gradle.kts delete mode 100644 aws-alexa/build.gradle create mode 100644 aws-alexa/build.gradle.kts delete mode 100644 aws-bom/build.gradle create mode 100644 aws-bom/build.gradle.kts rename aws-cdk/{build.gradle => build.gradle.kts} (75%) delete mode 100644 aws-common/build.gradle create mode 100644 aws-common/build.gradle.kts delete mode 100644 aws-distributed-configuration/build.gradle create mode 100644 aws-distributed-configuration/build.gradle.kts delete mode 100644 aws-parameter-store/build.gradle create mode 100644 aws-parameter-store/build.gradle.kts delete mode 100644 aws-sdk-v1/build.gradle create mode 100644 aws-sdk-v1/build.gradle.kts delete mode 100644 aws-sdk-v2/build.gradle create mode 100644 aws-sdk-v2/build.gradle.kts delete mode 100644 aws-secretsmanager/build.gradle create mode 100644 aws-secretsmanager/build.gradle.kts delete mode 100644 aws-service-discovery/build.gradle create mode 100644 aws-service-discovery/build.gradle.kts delete mode 100644 build.gradle create mode 100644 build.gradle.kts delete mode 100644 function-aws-alexa/build.gradle create mode 100644 function-aws-alexa/build.gradle.kts delete mode 100644 function-aws-api-proxy-test/build.gradle create mode 100644 function-aws-api-proxy-test/build.gradle.kts delete mode 100644 function-aws-api-proxy/build.gradle create mode 100644 function-aws-api-proxy/build.gradle.kts delete mode 100644 function-aws-custom-runtime/build.gradle create mode 100644 function-aws-custom-runtime/build.gradle.kts delete mode 100644 function-aws-test/build.gradle create mode 100644 function-aws-test/build.gradle.kts rename function-aws/{build.gradle => build.gradle.kts} (65%) delete mode 100644 function-client-aws/build.gradle create mode 100644 function-client-aws/build.gradle.kts delete mode 100644 settings.gradle create mode 100644 settings.gradle.kts delete mode 100644 test-suite-groovy/build.gradle create mode 100644 test-suite-groovy/build.gradle.kts delete mode 100644 test-suite-kotlin/build.gradle create mode 100644 test-suite-kotlin/build.gradle.kts delete mode 100644 test-suite/build.gradle create mode 100644 test-suite/build.gradle.kts diff --git a/aws-alexa-httpserver/build.gradle b/aws-alexa-httpserver/build.gradle deleted file mode 100644 index 6cebe19c2c..0000000000 --- a/aws-alexa-httpserver/build.gradle +++ /dev/null @@ -1,24 +0,0 @@ -plugins { - id("io.micronaut.build.internal.module") -} - -dependencies { - annotationProcessor libs.micronaut.validation - - implementation libs.micronaut.validation - - api project(":aws-alexa") - - implementation libs.micronaut.http.server - api(libs.managed.alexa.ask.sdk.core) - - testImplementation libs.micronaut.http.client - testImplementation libs.micronaut.http.server.netty - testImplementation libs.bouncycastle.provider - testImplementation (libs.alexa.ask.sdk) { - transitive(false) - } - testImplementation libs.alexa.ask.sdk.apache.client - - testRuntimeOnly libs.jcl.over.slf4j -} diff --git a/aws-alexa-httpserver/build.gradle.kts b/aws-alexa-httpserver/build.gradle.kts new file mode 100644 index 0000000000..10d83b53da --- /dev/null +++ b/aws-alexa-httpserver/build.gradle.kts @@ -0,0 +1,24 @@ +plugins { + id("io.micronaut.build.internal.module") +} + +dependencies { + annotationProcessor(mn.micronaut.validation) + + implementation(mn.micronaut.validation) + + api(project(":aws-alexa")) + + implementation(mn.micronaut.http.server) + api(libs.managed.alexa.ask.sdk.core) + + testImplementation(mn.micronaut.http.client) + testImplementation(mn.micronaut.http.server.netty) + testImplementation(libs.bouncycastle.provider) + testImplementation(libs.alexa.ask.sdk) { + isTransitive = false + } + testImplementation(libs.alexa.ask.sdk.apache.client) + + testRuntimeOnly(libs.jcl.over.slf4j) +} diff --git a/aws-alexa/build.gradle b/aws-alexa/build.gradle deleted file mode 100644 index 531999073f..0000000000 --- a/aws-alexa/build.gradle +++ /dev/null @@ -1,23 +0,0 @@ -plugins { - id("io.micronaut.build.internal.module") -} - -dependencies { - annotationProcessor libs.micronaut.validation - - implementation libs.micronaut.validation - - compileOnly(libs.alexa.ask.sdk) - api(libs.managed.alexa.ask.sdk.core) - - testAnnotationProcessor libs.micronaut.inject.java - testImplementation(libs.alexa.ask.sdk) { - transitive(false) - } - testImplementation libs.alexa.ask.sdk.apache.client - - testImplementation libs.micronaut.http.client - testImplementation libs.micronaut.http.server.netty - testImplementation libs.groovy.json - testRuntimeOnly libs.jcl.over.slf4j -} diff --git a/aws-alexa/build.gradle.kts b/aws-alexa/build.gradle.kts new file mode 100644 index 0000000000..258a6c6c91 --- /dev/null +++ b/aws-alexa/build.gradle.kts @@ -0,0 +1,23 @@ +plugins { + id("io.micronaut.build.internal.module") +} + +dependencies { + annotationProcessor(mn.micronaut.validation) + + implementation(mn.micronaut.validation) + + compileOnly(libs.alexa.ask.sdk) + api(libs.managed.alexa.ask.sdk.core) + + testAnnotationProcessor(mn.micronaut.inject.java) + testImplementation(libs.alexa.ask.sdk) { + isTransitive = false + } + testImplementation(libs.alexa.ask.sdk.apache.client) + + testImplementation(mn.micronaut.http.client) + testImplementation(mn.micronaut.http.server.netty) + testImplementation(libs.groovy.json) + testRuntimeOnly(libs.jcl.over.slf4j) +} diff --git a/aws-bom/build.gradle b/aws-bom/build.gradle deleted file mode 100644 index a2429aa5ba..0000000000 --- a/aws-bom/build.gradle +++ /dev/null @@ -1,3 +0,0 @@ -plugins { - id 'io.micronaut.build.internal.bom' -} diff --git a/aws-bom/build.gradle.kts b/aws-bom/build.gradle.kts new file mode 100644 index 0000000000..1f39ff0d93 --- /dev/null +++ b/aws-bom/build.gradle.kts @@ -0,0 +1,3 @@ +plugins { + id("io.micronaut.build.internal.bom") +} diff --git a/aws-cdk/build.gradle b/aws-cdk/build.gradle.kts similarity index 75% rename from aws-cdk/build.gradle rename to aws-cdk/build.gradle.kts index 7baf28ce60..ba084668c6 100644 --- a/aws-cdk/build.gradle +++ b/aws-cdk/build.gradle.kts @@ -2,8 +2,10 @@ plugins { id("io.micronaut.build.internal.module") } +val micronautVersion: String by project + dependencies { - api libs.aws.cdk.lib + api(libs.aws.cdk.lib) api("io.micronaut.starter:micronaut-starter-api:$micronautVersion") testImplementation(project(":function-aws-api-proxy")) } diff --git a/aws-common/build.gradle b/aws-common/build.gradle deleted file mode 100644 index be8b80acc7..0000000000 --- a/aws-common/build.gradle +++ /dev/null @@ -1,8 +0,0 @@ -plugins { - id("io.micronaut.build.internal.module") -} - -dependencies { - compileOnly libs.micronaut.runtime - testImplementation libs.micronaut.runtime -} diff --git a/aws-common/build.gradle.kts b/aws-common/build.gradle.kts new file mode 100644 index 0000000000..638495482a --- /dev/null +++ b/aws-common/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + id("io.micronaut.build.internal.module") +} + +dependencies { + compileOnly(mn.micronaut.runtime) + testImplementation(mn.micronaut.runtime) +} diff --git a/aws-distributed-configuration/build.gradle b/aws-distributed-configuration/build.gradle deleted file mode 100644 index 9011ebcf88..0000000000 --- a/aws-distributed-configuration/build.gradle +++ /dev/null @@ -1,9 +0,0 @@ -plugins { - id("io.micronaut.build.internal.module") -} - -dependencies { - api project(":aws-common") - api(libs.micronaut.discovery.client) - testImplementation libs.micronaut.http.server.netty -} diff --git a/aws-distributed-configuration/build.gradle.kts b/aws-distributed-configuration/build.gradle.kts new file mode 100644 index 0000000000..0acd343bf6 --- /dev/null +++ b/aws-distributed-configuration/build.gradle.kts @@ -0,0 +1,9 @@ +plugins { + id("io.micronaut.build.internal.module") +} + +dependencies { + api(project(":aws-common")) + api("io.micronaut.discovery:micronaut-discovery-client") + testImplementation(mn.micronaut.http.server.netty) +} diff --git a/aws-parameter-store/build.gradle b/aws-parameter-store/build.gradle deleted file mode 100644 index 73795f238b..0000000000 --- a/aws-parameter-store/build.gradle +++ /dev/null @@ -1,14 +0,0 @@ -plugins { - id("io.micronaut.build.internal.module") -} - -dependencies { - api libs.micronaut.discovery.client - api project(":aws-service-discovery") - api project(":aws-sdk-v2") - implementation libs.aws.ssm - - implementation libs.projectreactor - - testImplementation libs.micronaut.http.server.netty -} diff --git a/aws-parameter-store/build.gradle.kts b/aws-parameter-store/build.gradle.kts new file mode 100644 index 0000000000..604694ec31 --- /dev/null +++ b/aws-parameter-store/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + id("io.micronaut.build.internal.module") +} + +dependencies { + api("io.micronaut.discovery:micronaut-discovery-client") + api(project(":aws-service-discovery")) + api(project(":aws-sdk-v2")) + implementation(libs.aws.ssm) + implementation(libs.projectreactor) + testImplementation(mn.micronaut.http.server.netty) +} diff --git a/aws-sdk-v1/build.gradle b/aws-sdk-v1/build.gradle deleted file mode 100644 index 061e25395d..0000000000 --- a/aws-sdk-v1/build.gradle +++ /dev/null @@ -1,14 +0,0 @@ -plugins { - id("io.micronaut.build.internal.module") -} - -dependencies { - api platform(libs.boms.aws.java.sdk.v1) - api libs.managed.aws.java.sdk.core - - api project(":aws-common") - - runtimeOnly libs.jcl.over.slf4j - - testImplementation libs.micronaut.http.server.netty -} diff --git a/aws-sdk-v1/build.gradle.kts b/aws-sdk-v1/build.gradle.kts new file mode 100644 index 0000000000..1560ae9127 --- /dev/null +++ b/aws-sdk-v1/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + id("io.micronaut.build.internal.module") +} + +dependencies { + api(platform(libs.boms.aws.java.sdk.v1)) + api(libs.managed.aws.java.sdk.core) + + api(project(":aws-common")) + + runtimeOnly(libs.jcl.over.slf4j) + + testImplementation(mn.micronaut.http.server.netty) +} diff --git a/aws-sdk-v2/build.gradle b/aws-sdk-v2/build.gradle deleted file mode 100644 index 3ed7bc99e0..0000000000 --- a/aws-sdk-v2/build.gradle +++ /dev/null @@ -1,43 +0,0 @@ -plugins { - id("io.micronaut.build.internal.module") -} - -dependencies { - api platform(libs.boms.aws.java.sdk.v2) - api project(":aws-common") - - compileOnly libs.graal - - // Clients - compileOnly libs.awssdk.url.connection.client - compileOnly libs.awssdk.netty.nio.client - compileOnly libs.awssdk.apache.client - - // Services - compileOnly libs.awssdk.apigatewaymanagementapi - compileOnly libs.awssdk.s3 - compileOnly libs.awssdk.dynamodb - compileOnly libs.awssdk.ses - compileOnly libs.awssdk.sns - compileOnly libs.awssdk.sqs - compileOnly libs.awssdk.ssm - compileOnly libs.awssdk.secretsmanager - compileOnly libs.awssdk.servicediscovery - - // Tests - testAnnotationProcessor libs.micronaut.inject.java - testImplementation libs.awssdk.apigatewaymanagementapi - testImplementation libs.awssdk.servicediscovery - testImplementation libs.awssdk.url.connection.client - testImplementation libs.awssdk.netty.nio.client - testImplementation libs.awssdk.apache.client - testImplementation libs.awssdk.s3 - testImplementation libs.awssdk.dynamodb - testImplementation libs.awssdk.ses - testImplementation libs.awssdk.secretsmanager - testImplementation libs.awssdk.sns - testImplementation libs.awssdk.sqs - testImplementation libs.awssdk.ssm - testImplementation libs.awssdk.rekognition - testRuntimeOnly libs.jcl.over.slf4j -} diff --git a/aws-sdk-v2/build.gradle.kts b/aws-sdk-v2/build.gradle.kts new file mode 100644 index 0000000000..311bb56a36 --- /dev/null +++ b/aws-sdk-v2/build.gradle.kts @@ -0,0 +1,43 @@ +plugins { + id("io.micronaut.build.internal.module") +} + +dependencies { + api(platform(libs.boms.aws.java.sdk.v2)) + api(project(":aws-common")) + + compileOnly(libs.graal) + + // Clients + compileOnly(libs.awssdk.url.connection.client) + compileOnly(libs.awssdk.netty.nio.client) + compileOnly(libs.awssdk.apache.client) + + // Services + compileOnly(libs.awssdk.apigatewaymanagementapi) + compileOnly(libs.awssdk.s3) + compileOnly(libs.awssdk.dynamodb) + compileOnly(libs.awssdk.ses) + compileOnly(libs.awssdk.sns) + compileOnly(libs.awssdk.sqs) + compileOnly(libs.awssdk.ssm) + compileOnly(libs.awssdk.secretsmanager) + compileOnly(libs.awssdk.servicediscovery) + + // Tests + testAnnotationProcessor(mn.micronaut.inject.java) + testImplementation(libs.awssdk.apigatewaymanagementapi) + testImplementation(libs.awssdk.servicediscovery) + testImplementation(libs.awssdk.url.connection.client) + testImplementation(libs.awssdk.netty.nio.client) + testImplementation(libs.awssdk.apache.client) + testImplementation(libs.awssdk.s3) + testImplementation(libs.awssdk.dynamodb) + testImplementation(libs.awssdk.ses) + testImplementation(libs.awssdk.secretsmanager) + testImplementation(libs.awssdk.sns) + testImplementation(libs.awssdk.sqs) + testImplementation(libs.awssdk.ssm) + testImplementation(libs.awssdk.rekognition) + testRuntimeOnly(libs.jcl.over.slf4j) +} diff --git a/aws-secretsmanager/build.gradle b/aws-secretsmanager/build.gradle deleted file mode 100644 index 8ed65ffb58..0000000000 --- a/aws-secretsmanager/build.gradle +++ /dev/null @@ -1,9 +0,0 @@ -plugins { - id("io.micronaut.build.internal.module") -} - -dependencies { - api project(":aws-sdk-v2") - api project(":aws-distributed-configuration") - api libs.awssdk.secretsmanager -} diff --git a/aws-secretsmanager/build.gradle.kts b/aws-secretsmanager/build.gradle.kts new file mode 100644 index 0000000000..9b46e86662 --- /dev/null +++ b/aws-secretsmanager/build.gradle.kts @@ -0,0 +1,9 @@ +plugins { + id("io.micronaut.build.internal.module") +} + +dependencies { + api(project(":aws-sdk-v2")) + api(project(":aws-distributed-configuration")) + api(libs.awssdk.secretsmanager) +} diff --git a/aws-service-discovery/build.gradle b/aws-service-discovery/build.gradle deleted file mode 100644 index 51a0755fe5..0000000000 --- a/aws-service-discovery/build.gradle +++ /dev/null @@ -1,13 +0,0 @@ -plugins { - id("io.micronaut.build.internal.module") -} - -dependencies { - api libs.micronaut.discovery.client - api project(":aws-sdk-v2") - - implementation libs.awssdk.servicediscovery - - testImplementation libs.projectreactor - testImplementation libs.micronaut.http.server.netty -} diff --git a/aws-service-discovery/build.gradle.kts b/aws-service-discovery/build.gradle.kts new file mode 100644 index 0000000000..2439c00e92 --- /dev/null +++ b/aws-service-discovery/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + id("io.micronaut.build.internal.module") +} + +dependencies { + api("io.micronaut.discovery:micronaut-discovery-client") + api(project(":aws-sdk-v2")) + implementation(libs.awssdk.servicediscovery) + testImplementation(libs.projectreactor) + testImplementation(mn.micronaut.http.server.netty) +} diff --git a/build.gradle b/build.gradle deleted file mode 100644 index cf34f01f06..0000000000 --- a/build.gradle +++ /dev/null @@ -1,5 +0,0 @@ -plugins { - id "io.micronaut.build.internal.docs" - id "io.micronaut.build.internal.dependency-updates" - id "io.micronaut.build.internal.quality-reporting" -} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000000..e97ee10faf --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,5 @@ +plugins { + id("io.micronaut.build.internal.docs") + id("io.micronaut.build.internal.dependency-updates") + id("io.micronaut.build.internal.quality-reporting") +} diff --git a/function-aws-alexa/build.gradle b/function-aws-alexa/build.gradle deleted file mode 100644 index 0f241c2b49..0000000000 --- a/function-aws-alexa/build.gradle +++ /dev/null @@ -1,23 +0,0 @@ -plugins { - id("io.micronaut.build.internal.module") -} - -dependencies { - annotationProcessor libs.micronaut.validation - - implementation libs.micronaut.runtime - implementation libs.micronaut.validation - - implementation project(":function-aws") - - api libs.managed.alexa.ask.sdk.lambda - api project(":aws-alexa") - - runtimeOnly libs.jcl.over.slf4j - - testAnnotationProcessor libs.micronaut.inject.java - testImplementation (libs.alexa.ask.sdk) { - transitive(false) - } - testImplementation libs.alexa.ask.sdk.apache.client -} diff --git a/function-aws-alexa/build.gradle.kts b/function-aws-alexa/build.gradle.kts new file mode 100644 index 0000000000..1cf7d3edbd --- /dev/null +++ b/function-aws-alexa/build.gradle.kts @@ -0,0 +1,23 @@ +plugins { + id("io.micronaut.build.internal.module") +} + +dependencies { + annotationProcessor(mn.micronaut.validation) + + implementation(mn.micronaut.runtime) + implementation(mn.micronaut.validation) + + implementation(project(":function-aws")) + + api(libs.managed.alexa.ask.sdk.lambda) + api(project(":aws-alexa")) + + runtimeOnly(libs.jcl.over.slf4j) + + testAnnotationProcessor(mn.micronaut.inject.java) + testImplementation(libs.alexa.ask.sdk) { + isTransitive = false + } + testImplementation(libs.alexa.ask.sdk.apache.client) +} diff --git a/function-aws-api-proxy-test/build.gradle b/function-aws-api-proxy-test/build.gradle deleted file mode 100644 index 4692497aee..0000000000 --- a/function-aws-api-proxy-test/build.gradle +++ /dev/null @@ -1,10 +0,0 @@ -plugins { - id("io.micronaut.build.internal.module") -} - -dependencies { - api libs.micronaut.http.server - api project(":function-aws-api-proxy") - implementation libs.jetty.server - testImplementation libs.micronaut.http.client -} diff --git a/function-aws-api-proxy-test/build.gradle.kts b/function-aws-api-proxy-test/build.gradle.kts new file mode 100644 index 0000000000..e1fc31f764 --- /dev/null +++ b/function-aws-api-proxy-test/build.gradle.kts @@ -0,0 +1,10 @@ +plugins { + id("io.micronaut.build.internal.module") +} + +dependencies { + api(mn.micronaut.http.server) + api(project(":function-aws-api-proxy")) + implementation(libs.jetty.server) + testImplementation(mn.micronaut.http.client) +} diff --git a/function-aws-api-proxy/build.gradle b/function-aws-api-proxy/build.gradle deleted file mode 100644 index a939333894..0000000000 --- a/function-aws-api-proxy/build.gradle +++ /dev/null @@ -1,42 +0,0 @@ -plugins { - id("io.micronaut.build.internal.module") -} - -dependencies { - annotationProcessor libs.micronaut.graal - - compileOnly libs.micronaut.security - - implementation libs.micronaut.http.netty - - implementation libs.projectreactor - - api libs.micronaut.http.server - api(libs.managed.aws.serverless.core) { - exclude group:'javax.servlet', module:'javax.servlet-api' - exclude group:'com.fasterxml.jackson.module', module:'jackson-module-afterburner' - exclude group: "commons-logging" - } - api libs.managed.jcl.over.slf4j - api project(":function-aws") - api project(":aws-common") - - testAnnotationProcessor libs.micronaut.validation - testImplementation libs.micronaut.validation - - testImplementation libs.micronaut.inject.java - testImplementation libs.micronaut.http.client - testImplementation libs.micronaut.security - - testImplementation libs.micronaut.views.handlebars - - testImplementation libs.jackson.afterburner - testImplementation libs.servlet.api - testImplementation libs.fileupload -} - -spotless { - java { - targetExclude '**/io/micronaut/function/aws/proxy/QueryStringDecoder.java' - } -} diff --git a/function-aws-api-proxy/build.gradle.kts b/function-aws-api-proxy/build.gradle.kts new file mode 100644 index 0000000000..cc053f2e5e --- /dev/null +++ b/function-aws-api-proxy/build.gradle.kts @@ -0,0 +1,42 @@ +plugins { + id("io.micronaut.build.internal.module") +} + +dependencies { + annotationProcessor(mn.micronaut.graal) + + compileOnly(mn.micronaut.security) + + implementation(mn.micronaut.http.netty) + + implementation(libs.projectreactor) + + api(mn.micronaut.http.server) + api(libs.managed.aws.serverless.core) { + exclude(group = "javax.servlet", module = "javax.servlet-api") + exclude(group = "com.fasterxml.jackson.module", module = "jackson-module-afterburner") + exclude(group = "commons-logging") + } + api(libs.managed.jcl.over.slf4j) + api(project(":function-aws")) + api(project(":aws-common")) + + testAnnotationProcessor(mn.micronaut.validation) + testImplementation(mn.micronaut.validation) + + testImplementation(mn.micronaut.inject.java) + testImplementation(mn.micronaut.http.client) + testImplementation(mn.micronaut.security) + + testImplementation(mn.micronaut.views.handlebars) + + testImplementation(libs.jackson.afterburner) + testImplementation(libs.servlet.api) + testImplementation(libs.fileupload) +} + +spotless { + java { + targetExclude("**/io/micronaut/function/aws/proxy/QueryStringDecoder.java") + } +} diff --git a/function-aws-custom-runtime/build.gradle b/function-aws-custom-runtime/build.gradle deleted file mode 100644 index 2fd7264794..0000000000 --- a/function-aws-custom-runtime/build.gradle +++ /dev/null @@ -1,17 +0,0 @@ -plugins { - id("io.micronaut.build.internal.module") -} - -dependencies { - annotationProcessor libs.micronaut.graal - compileOnly project(":function-aws-api-proxy") - - api libs.micronaut.http.client - api libs.managed.aws.lambda.events - - testImplementation project(":function-aws-api-proxy"), { - exclude group:'com.fasterxml.jackson.module', module:'jackson-module-afterburner' - } - testImplementation libs.micronaut.inject.java - testImplementation libs.micronaut.http.server.netty -} diff --git a/function-aws-custom-runtime/build.gradle.kts b/function-aws-custom-runtime/build.gradle.kts new file mode 100644 index 0000000000..22a5b83d27 --- /dev/null +++ b/function-aws-custom-runtime/build.gradle.kts @@ -0,0 +1,17 @@ +plugins { + id("io.micronaut.build.internal.module") +} + +dependencies { + annotationProcessor(mn.micronaut.graal) + compileOnly(project(":function-aws-api-proxy")) + + api(mn.micronaut.http.client) + api(libs.managed.aws.lambda.events) + + testImplementation(project(":function-aws-api-proxy")) { + exclude(group = "com.fasterxml.jackson.module", module = "jackson-module-afterburner") + } + testImplementation(mn.micronaut.inject.java) + testImplementation(mn.micronaut.http.server.netty) +} diff --git a/function-aws-test/build.gradle b/function-aws-test/build.gradle deleted file mode 100644 index a7eb8ceb32..0000000000 --- a/function-aws-test/build.gradle +++ /dev/null @@ -1,11 +0,0 @@ -plugins { - id("io.micronaut.build.internal.module") -} - -dependencies { - implementation libs.micronaut.test.junit5 - - api project(":function-aws") - api libs.micronaut.function - testAnnotationProcessor libs.micronaut.inject.java -} diff --git a/function-aws-test/build.gradle.kts b/function-aws-test/build.gradle.kts new file mode 100644 index 0000000000..799040d238 --- /dev/null +++ b/function-aws-test/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + id("io.micronaut.build.internal.module") +} + +dependencies { + implementation(mn.micronaut.test.junit5) + + api(project(":function-aws")) + api(mn.micronaut.function) + testAnnotationProcessor(mn.micronaut.inject.java) +} diff --git a/function-aws/build.gradle b/function-aws/build.gradle.kts similarity index 65% rename from function-aws/build.gradle rename to function-aws/build.gradle.kts index da5d0d2480..63f0e04342 100644 --- a/function-aws/build.gradle +++ b/function-aws/build.gradle.kts @@ -3,10 +3,10 @@ plugins { } dependencies { - api libs.micronaut.function - api libs.managed.aws.lambda.core + api(mn.micronaut.function) + api(libs.managed.aws.lambda.core) - testImplementation(libs.micronaut.mongo.sync) + testImplementation(mn.micronaut.mongo.sync) testImplementation(libs.testcontainers.spock) testImplementation(libs.testcontainers.mongodb) testImplementation(libs.testcontainers) diff --git a/function-client-aws/build.gradle b/function-client-aws/build.gradle deleted file mode 100644 index e1b9244b46..0000000000 --- a/function-client-aws/build.gradle +++ /dev/null @@ -1,17 +0,0 @@ -plugins { - id("io.micronaut.build.internal.module") -} - -dependencies { - api project(":aws-sdk-v1") - implementation "com.amazonaws:aws-java-sdk-lambda" - implementation libs.projectreactor - api libs.micronaut.function.client - - testAnnotationProcessor libs.micronaut.inject.java - testImplementation libs.micronaut.inject.java - testImplementation libs.micronaut.http.server.netty - testImplementation libs.micronaut.function.web - testImplementation libs.micronaut.function.groovy - testImplementation libs.micronaut.runtime.groovy -} diff --git a/function-client-aws/build.gradle.kts b/function-client-aws/build.gradle.kts new file mode 100644 index 0000000000..e76a301c0d --- /dev/null +++ b/function-client-aws/build.gradle.kts @@ -0,0 +1,17 @@ +plugins { + id("io.micronaut.build.internal.module") +} + +dependencies { + api(project(":aws-sdk-v1")) + implementation("com.amazonaws:aws-java-sdk-lambda") + implementation(libs.projectreactor) + api(mn.micronaut.function.client) + + testAnnotationProcessor(mn.micronaut.inject.java) + testImplementation(mn.micronaut.inject.java) + testImplementation(mn.micronaut.http.server.netty) + testImplementation(mn.micronaut.function.web) + testImplementation(mn.micronaut.function.groovy) + testImplementation(mn.micronaut.runtime.groovy) +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fb74b703dd..747fc2ff96 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -55,25 +55,6 @@ managed-awssdk-secretsmanager = { module = 'software.amazon.awssdk:secretsmanage managed-aws-serverless-core = { module = 'com.amazonaws.serverless:aws-serverless-java-container-core', version.ref = 'managed-aws-serverless-core' } managed-jcl-over-slf4j = { module = 'org.slf4j:jcl-over-slf4j', version.ref = 'slf4j' } -micronaut-discovery-client = { module = 'io.micronaut.discovery:micronaut-discovery-client' } -micronaut-function = { module = 'io.micronaut:micronaut-function' } -micronaut-function-client = { module = 'io.micronaut:micronaut-function-client' } -micronaut-function-groovy = { module = 'io.micronaut.groovy:micronaut-function-groovy' } -micronaut-function-web = { module = 'io.micronaut:micronaut-function-web' } -micronaut-http-client = { module = 'io.micronaut:micronaut-http-client' } -micronaut-http-netty = { module = 'io.micronaut:micronaut-http-netty' } -micronaut-http-server = { module = 'io.micronaut:micronaut-http-server' } -micronaut-http-server-netty = { module = 'io.micronaut:micronaut-http-server-netty' } -micronaut-graal = { module = 'io.micronaut:micronaut-graal' } -micronaut-inject-java = { module = 'io.micronaut:micronaut-inject-java' } -micronaut-mongo-sync = { module = 'io.micronaut.mongodb:micronaut-mongo-sync' } -micronaut-runtime = { module = 'io.micronaut:micronaut-runtime' } -micronaut-runtime-groovy = { module = 'io.micronaut.groovy:micronaut-runtime-groovy' } -micronaut-security = { module = 'io.micronaut.security:micronaut-security' } -micronaut-test-junit5 = { module = 'io.micronaut.test:micronaut-test-junit5' } -micronaut-validation = { module = 'io.micronaut:micronaut-validation' } -micronaut-views-handlebars = { module = 'io.micronaut.views:micronaut-views-handlebars' } - projectreactor = { module = 'io.projectreactor:reactor-core' } servlet-api = { module = 'javax.servlet:servlet-api', version.ref = 'servlet-api' } testcontainers = { module = 'org.testcontainers:testcontainers' } diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 9f8df9e3d2..0000000000 --- a/settings.gradle +++ /dev/null @@ -1,34 +0,0 @@ -pluginManagement { - repositories { - gradlePluginPortal() - mavenCentral() - } -} - -plugins { - id 'io.micronaut.build.shared.settings' version '5.3.14' -} - -rootProject.name = 'aws-parent' - -include 'aws-bom' -include 'function-aws' -include 'function-client-aws' -include 'function-aws-api-proxy' -include 'function-aws-api-proxy-test' -include 'aws-secretsmanager' -include 'aws-alexa' -include 'aws-distributed-configuration' -include 'aws-parameter-store' -include 'aws-service-discovery' -include 'aws-alexa-httpserver' -include 'function-aws-alexa' -include 'function-aws-custom-runtime' -include 'aws-common' -include 'aws-sdk-v1' -include 'aws-sdk-v2' -include 'aws-cdk' -include 'function-aws-test' -include 'test-suite' -include 'test-suite-groovy' -include 'test-suite-kotlin' diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000000..64be1a47a4 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,38 @@ +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + } +} + +plugins { + id("io.micronaut.build.shared.settings") version "5.3.14" +} + +rootProject.name = "aws-parent" + +include("aws-bom") +include("function-aws") +include("function-client-aws") +include("function-aws-api-proxy") +include("function-aws-api-proxy-test") +include("aws-secretsmanager") +include("aws-alexa") +include("aws-distributed-configuration") +include("aws-parameter-store") +include("aws-service-discovery") +include("aws-alexa-httpserver") +include("function-aws-alexa") +include("function-aws-custom-runtime") +include("aws-common") +include("aws-sdk-v1") +include("aws-sdk-v2") +include("aws-cdk") +include("function-aws-test") +include("test-suite") +include("test-suite-groovy") +include("test-suite-kotlin") + +configure { + importMicronautCatalog() +} diff --git a/test-suite-groovy/build.gradle b/test-suite-groovy/build.gradle deleted file mode 100644 index 8bd9f4b821..0000000000 --- a/test-suite-groovy/build.gradle +++ /dev/null @@ -1,28 +0,0 @@ -plugins { - id 'groovy' - id 'java-library' -} -repositories { - mavenCentral() -} - -dependencies { - testCompileOnly("io.micronaut:micronaut-inject-groovy:$micronautVersion") - testImplementation("org.spockframework:spock-core:${spockVersion}") { - exclude module:'groovy-all' - } - testImplementation "io.micronaut.test:micronaut-test-spock:$micronautTestVersion" - - testImplementation(platform("io.micronaut:micronaut-bom:$micronautVersion")) - testImplementation project(":function-aws") - testImplementation project(":function-client-aws") -} - -tasks.named('test') { - useJUnitPlatform() -} - -java { - sourceCompatibility = JavaVersion.toVersion("1.8") - targetCompatibility = JavaVersion.toVersion("1.8") -} diff --git a/test-suite-groovy/build.gradle.kts b/test-suite-groovy/build.gradle.kts new file mode 100644 index 0000000000..c13c6d7fe7 --- /dev/null +++ b/test-suite-groovy/build.gradle.kts @@ -0,0 +1,34 @@ +plugins { + id("groovy") + id("java-library") +} + +repositories { + mavenCentral() +} + +val micronautVersion: String by project +val micronautTestVersion: String by project +val spockVersion: String by project + +dependencies { + testCompileOnly("io.micronaut:micronaut-inject-groovy:$micronautVersion") + testImplementation("org.spockframework:spock-core:${spockVersion}") { + exclude(module = "groovy-all") + } + testImplementation("io.micronaut.test:micronaut-test-spock:$micronautTestVersion") + testImplementation(platform("io.micronaut:micronaut-bom:$micronautVersion")) + testImplementation(project(":function-aws")) + testImplementation(project(":function-client-aws")) +} + +tasks { + named("test", Test::class) { + useJUnitPlatform() + } +} + +java { + sourceCompatibility = JavaVersion.toVersion("1.8") + targetCompatibility = JavaVersion.toVersion("1.8") +} diff --git a/test-suite-kotlin/build.gradle b/test-suite-kotlin/build.gradle deleted file mode 100644 index c705f2fb84..0000000000 --- a/test-suite-kotlin/build.gradle +++ /dev/null @@ -1,37 +0,0 @@ -plugins { - id "org.jetbrains.kotlin.jvm" version "1.7.10" - id("org.jetbrains.kotlin.kapt") version "1.7.10" -} - -repositories { - mavenCentral() -} - -dependencies { - kaptTest "io.micronaut:micronaut-inject-java:$micronautVersion" - - testImplementation(platform("io.micronaut:micronaut-bom:$micronautVersion")) - testImplementation("org.junit.jupiter:junit-jupiter-api") - testImplementation "io.micronaut.test:micronaut-test-junit5" - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") - - testImplementation project(":function-aws") - testImplementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.10' - testImplementation project(":function-client-aws") -} - -tasks.named('test') { - useJUnitPlatform() -} - -tasks.named("compileTestKotlin") { - kotlinOptions { - jvmTarget = '1.8' - javaParameters = true - } -} - -java { - sourceCompatibility = JavaVersion.toVersion("1.8") - targetCompatibility = JavaVersion.toVersion("1.8") -} diff --git a/test-suite-kotlin/build.gradle.kts b/test-suite-kotlin/build.gradle.kts new file mode 100644 index 0000000000..ca51502189 --- /dev/null +++ b/test-suite-kotlin/build.gradle.kts @@ -0,0 +1,41 @@ +plugins { + id("org.jetbrains.kotlin.jvm") version ("1.7.10") + id("org.jetbrains.kotlin.kapt") version ("1.7.10") +} + +repositories { + mavenCentral() +} + +val micronautVersion: String by project + +dependencies { + kaptTest("io.micronaut:micronaut-inject-java:$micronautVersion") + + testImplementation(platform("io.micronaut:micronaut-bom:$micronautVersion")) + testImplementation("org.junit.jupiter:junit-jupiter-api") + testImplementation("io.micronaut.test:micronaut-test-junit5") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") + + testImplementation(project(":function-aws")) + testImplementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.10") + testImplementation(project(":function-client-aws")) +} + +tasks { + named("test", Test::class) { + useJUnitPlatform() + } + + named("compileTestKotlin", org.jetbrains.kotlin.gradle.tasks.KotlinCompile::class) { + kotlinOptions { + jvmTarget = "1.8" + javaParameters = true + } + } +} + +java { + sourceCompatibility = JavaVersion.toVersion("1.8") + targetCompatibility = JavaVersion.toVersion("1.8") +} diff --git a/test-suite/build.gradle b/test-suite/build.gradle deleted file mode 100644 index f176e1c004..0000000000 --- a/test-suite/build.gradle +++ /dev/null @@ -1,33 +0,0 @@ -plugins { - id 'java-library' -} -repositories { - mavenCentral() -} -dependencies { - testAnnotationProcessor(platform("io.micronaut:micronaut-bom:$micronautVersion")) - testAnnotationProcessor "io.micronaut:micronaut-inject-java" - - testImplementation(platform("io.micronaut:micronaut-bom:$micronautVersion")) - testImplementation("org.junit.jupiter:junit-jupiter-api") - testImplementation "io.micronaut.test:micronaut-test-junit5" - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") - - testImplementation project(":function-aws") - testImplementation project(":function-client-aws") -} - -tasks.named("test") { - systemProperty "aws.accessKeyId", 'XXX' - systemProperty "aws.secretKey", 'YYY' - systemProperty "aws.region", 'us-east-1' -} - -tasks.named('test') { - useJUnitPlatform() -} - -java { - sourceCompatibility = JavaVersion.toVersion("1.8") - targetCompatibility = JavaVersion.toVersion("1.8") -} \ No newline at end of file diff --git a/test-suite/build.gradle.kts b/test-suite/build.gradle.kts new file mode 100644 index 0000000000..398c51132d --- /dev/null +++ b/test-suite/build.gradle.kts @@ -0,0 +1,37 @@ +plugins { + id("java-library") +} + +repositories { + mavenCentral() +} + +val micronautVersion: String by project + +dependencies { + testAnnotationProcessor(platform("io.micronaut:micronaut-bom:$micronautVersion")) + testAnnotationProcessor("io.micronaut:micronaut-inject-java") + + testImplementation(platform("io.micronaut:micronaut-bom:$micronautVersion")) + testImplementation("org.junit.jupiter:junit-jupiter-api") + testImplementation("io.micronaut.test:micronaut-test-junit5") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") + + testImplementation(project(":function-aws")) + testImplementation(project(":function-client-aws")) +} + +tasks { + named("test", Test::class) { + useJUnitPlatform() + + systemProperty("aws.accessKeyId", "XXX") + systemProperty("aws.secretKey", "YYY") + systemProperty("aws.region", "us-east-1") + } +} + +java { + sourceCompatibility = JavaVersion.toVersion("1.8") + targetCompatibility = JavaVersion.toVersion("1.8") +} From 82a6023006b14cca9e810b6bb9f25b5a80a45e56 Mon Sep 17 00:00:00 2001 From: Tim Yates Date: Wed, 7 Sep 2022 12:19:23 +0100 Subject: [PATCH 13/51] Switch to using catalog discovery client (#1454) --- aws-distributed-configuration/build.gradle.kts | 2 +- aws-parameter-store/build.gradle.kts | 2 +- aws-service-discovery/build.gradle.kts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/aws-distributed-configuration/build.gradle.kts b/aws-distributed-configuration/build.gradle.kts index 0acd343bf6..a1231c4edb 100644 --- a/aws-distributed-configuration/build.gradle.kts +++ b/aws-distributed-configuration/build.gradle.kts @@ -4,6 +4,6 @@ plugins { dependencies { api(project(":aws-common")) - api("io.micronaut.discovery:micronaut-discovery-client") + api(mn.micronaut.discovery) testImplementation(mn.micronaut.http.server.netty) } diff --git a/aws-parameter-store/build.gradle.kts b/aws-parameter-store/build.gradle.kts index 604694ec31..1f4db50878 100644 --- a/aws-parameter-store/build.gradle.kts +++ b/aws-parameter-store/build.gradle.kts @@ -3,7 +3,7 @@ plugins { } dependencies { - api("io.micronaut.discovery:micronaut-discovery-client") + api(mn.micronaut.discovery) api(project(":aws-service-discovery")) api(project(":aws-sdk-v2")) implementation(libs.aws.ssm) diff --git a/aws-service-discovery/build.gradle.kts b/aws-service-discovery/build.gradle.kts index 2439c00e92..dabe66a6d6 100644 --- a/aws-service-discovery/build.gradle.kts +++ b/aws-service-discovery/build.gradle.kts @@ -3,7 +3,7 @@ plugins { } dependencies { - api("io.micronaut.discovery:micronaut-discovery-client") + api(mn.micronaut.discovery) api(project(":aws-sdk-v2")) implementation(libs.awssdk.servicediscovery) testImplementation(libs.projectreactor) From b3d8fe21b1cbf5c1803d5fd2062ec00fbbf8ef66 Mon Sep 17 00:00:00 2001 From: Dean Wette Date: Wed, 7 Sep 2022 06:20:11 -0500 Subject: [PATCH 14/51] doc: Documentation for Api Gateway Management API factory beans (#1455) closes #1424 --- .../docs/guide/sdkv2/apiGatewayManagementApi.adoc | 15 +++++++++++++++ src/main/docs/guide/toc.yml | 1 + 2 files changed, 16 insertions(+) create mode 100644 src/main/docs/guide/sdkv2/apiGatewayManagementApi.adoc diff --git a/src/main/docs/guide/sdkv2/apiGatewayManagementApi.adoc b/src/main/docs/guide/sdkv2/apiGatewayManagementApi.adoc new file mode 100644 index 0000000000..67782d553c --- /dev/null +++ b/src/main/docs/guide/sdkv2/apiGatewayManagementApi.adoc @@ -0,0 +1,15 @@ +To use an API Gateway Management Api client, add the following dependency: + +dependency:apigatewaymanagementapi[groupId="software.amazon.awssdk"] + +Then, the following beans will be created: + +* `software.amazon.awssdk.services.apigatewaymanagementapi.ApiGatewayManagementApiClientBuilder` +* `software.amazon.awssdk.services.apigatewaymanagementapi.ApiGatewayManagementApiClient`. + +And: + +* `software.amazon.awssdk.services.apigatewaymanagementapi.ApiGatewayManagementApiAsyncClientBuilder` +* `software.amazon.awssdk.services.apigatewaymanagementapi.ApiGatewayManagementApiAsyncClient`. + +The HTTP client, credentials and region will be configured as per described in the <>. diff --git a/src/main/docs/guide/toc.yml b/src/main/docs/guide/toc.yml index 1706a8af1a..178f7eb5a1 100644 --- a/src/main/docs/guide/toc.yml +++ b/src/main/docs/guide/toc.yml @@ -20,6 +20,7 @@ sdkv2: nettyClient: Netty client awsCredentials: Supplying AWS credentials awsRegionSelection: AWS region selection + apiGatewayManagementApi: API Gateway Management API Client s3: S3 dynamodb: Dynamo DB ses: SES From cb01fd050bc1b1138fdf1c170e52b015d628893c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 12 Sep 2022 17:35:30 +0200 Subject: [PATCH 15/51] fix(deps): update dependency software.amazon.awscdk:aws-cdk-lib to v2.41.0 (#1461) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 747fc2ff96..24221dead6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ managed-aws-java-sdk-v2 = '2.17.267' managed-aws-lambda = '1.2.1' managed-aws-lambda-events = '3.11.0' managed-aws-serverless-core = '1.8.2' -managed-aws-cdk-lib='2.40.0' +managed-aws-cdk-lib='2.41.0' servlet-api = "2.5" slf4j = "1.7.36" From f6dc945faa8c0b85f5fb2b574f00e19ecf4fb5dc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 12 Sep 2022 17:35:44 +0200 Subject: [PATCH 16/51] fix(deps): update micronautversion to v3.6.3 (#1460) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index f555ed0dda..446aa3be61 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ projectVersion=3.9.0-SNAPSHOT projectGroup=io.micronaut.aws micronautDocsVersion=2.0.0 -micronautVersion=3.6.2 +micronautVersion=3.6.3 micronautTestVersion=3.5.0 groovyVersion=3.0.10 From e204f02a568bd375e0875e210cf07bfae2c66454 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 12 Sep 2022 17:35:57 +0200 Subject: [PATCH 17/51] fix(deps): update aws-java-sdk-v2 monorepo to v2.17.271 (#1459) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 24221dead6..b6ece5e59a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,7 @@ fileupload = '0.0.5' jetty = '9.4.48.v20220622' managed-alexa-ask-sdk = "2.44.0" managed-aws-java-sdk-v1 = '1.12.296' -managed-aws-java-sdk-v2 = '2.17.267' +managed-aws-java-sdk-v2 = '2.17.271' managed-aws-lambda = '1.2.1' managed-aws-lambda-events = '3.11.0' managed-aws-serverless-core = '1.8.2' From c072274be73a58df8729ca99a5131792f509dd0c Mon Sep 17 00:00:00 2001 From: Nemanja Mikic Date: Mon, 12 Sep 2022 17:37:08 +0200 Subject: [PATCH 18/51] feat: Cloudwatch logging (#1438) --- aws-cloudwatch-logging/build.gradle.kts | 19 + .../logging/CloudWatchJsonFormatter.java | 43 +++ .../logging/CloudWatchLoggingAppender.java | 362 ++++++++++++++++++ .../logging/CloudWatchLoggingClient.java | 129 +++++++ .../aws/cloudwatch/logging/package-info.java | 22 ++ .../CloudWatchLoggingAppenderSpec.groovy | 282 ++++++++++++++ .../logging/CloudwatchLoggingSpec.groovy | 181 +++++++++ .../aws/cloudwatch/logging/MockAppender.java | 30 ++ .../src/test/resources/application.yml | 3 + .../src/test/resources/logback.xml | 23 ++ aws-sdk-v2/build.gradle.kts | 2 + .../CloudwatchLogsClientFactory.java | 90 +++++ .../service/CloudWatchLogsClientSpec.groovy | 24 ++ gradle/libs.versions.toml | 4 + settings.gradle.kts | 1 + .../docs/guide/sdkv2/cloudWatchLogging.adoc | 17 + .../cloudWatchLogging/browsingTheLogs.adoc | 3 + .../cloudWatchLogging/emergencyAppender.adoc | 35 ++ .../logbackConfiguration.adoc | 28 ++ .../openTelemetryAndLogging.adoc | 32 ++ .../setCloudWatchLogging.adoc | 42 ++ src/main/docs/guide/toc.yml | 7 + src/main/docs/resources/img/logs.png | Bin 0 -> 303540 bytes 23 files changed, 1379 insertions(+) create mode 100644 aws-cloudwatch-logging/build.gradle.kts create mode 100644 aws-cloudwatch-logging/src/main/java/io/micronaut/aws/cloudwatch/logging/CloudWatchJsonFormatter.java create mode 100644 aws-cloudwatch-logging/src/main/java/io/micronaut/aws/cloudwatch/logging/CloudWatchLoggingAppender.java create mode 100644 aws-cloudwatch-logging/src/main/java/io/micronaut/aws/cloudwatch/logging/CloudWatchLoggingClient.java create mode 100644 aws-cloudwatch-logging/src/main/java/io/micronaut/aws/cloudwatch/logging/package-info.java create mode 100644 aws-cloudwatch-logging/src/test/groovy/io/micronaut/aws/cloudwatch/logging/CloudWatchLoggingAppenderSpec.groovy create mode 100644 aws-cloudwatch-logging/src/test/groovy/io/micronaut/aws/cloudwatch/logging/CloudwatchLoggingSpec.groovy create mode 100644 aws-cloudwatch-logging/src/test/java/io/micronaut/aws/cloudwatch/logging/MockAppender.java create mode 100644 aws-cloudwatch-logging/src/test/resources/application.yml create mode 100644 aws-cloudwatch-logging/src/test/resources/logback.xml create mode 100644 aws-sdk-v2/src/main/java/io/micronaut/aws/sdk/v2/service/cloudwatchlogs/CloudwatchLogsClientFactory.java create mode 100644 aws-sdk-v2/src/test/groovy/io/micronaut/aws/sdk/v2/service/CloudWatchLogsClientSpec.groovy create mode 100644 src/main/docs/guide/sdkv2/cloudWatchLogging.adoc create mode 100644 src/main/docs/guide/sdkv2/cloudWatchLogging/browsingTheLogs.adoc create mode 100644 src/main/docs/guide/sdkv2/cloudWatchLogging/emergencyAppender.adoc create mode 100644 src/main/docs/guide/sdkv2/cloudWatchLogging/logbackConfiguration.adoc create mode 100644 src/main/docs/guide/sdkv2/cloudWatchLogging/openTelemetryAndLogging.adoc create mode 100644 src/main/docs/guide/sdkv2/cloudWatchLogging/setCloudWatchLogging.adoc create mode 100644 src/main/docs/resources/img/logs.png diff --git a/aws-cloudwatch-logging/build.gradle.kts b/aws-cloudwatch-logging/build.gradle.kts new file mode 100644 index 0000000000..1555a71ef1 --- /dev/null +++ b/aws-cloudwatch-logging/build.gradle.kts @@ -0,0 +1,19 @@ +plugins { + id("io.micronaut.build.internal.module") +} + +dependencies { + api(project(":aws-sdk-v2")) + implementation(libs.logback.json.classic) + api(libs.awssdk.cloudwatchlogs) + api(mn.micronaut.runtime) + api(mn.micronaut.serde.jackson) + +} + +// TODO temporarily disable binary compatibility checks +micronautBuild { + binaryCompatibility { + enabled.set(false) + } +} diff --git a/aws-cloudwatch-logging/src/main/java/io/micronaut/aws/cloudwatch/logging/CloudWatchJsonFormatter.java b/aws-cloudwatch-logging/src/main/java/io/micronaut/aws/cloudwatch/logging/CloudWatchJsonFormatter.java new file mode 100644 index 0000000000..b304261356 --- /dev/null +++ b/aws-cloudwatch-logging/src/main/java/io/micronaut/aws/cloudwatch/logging/CloudWatchJsonFormatter.java @@ -0,0 +1,43 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.aws.cloudwatch.logging; + +import ch.qos.logback.contrib.json.JsonFormatter; +import io.micronaut.serde.ObjectMapper; +import io.micronaut.core.annotation.Internal; + +import java.io.IOException; +import java.util.Map; + +/** + * CloudWatch's implementation of the {@link JsonFormatter}. + * + * @author Nemanja Mikic + * @since 3.9.0 + */ +@Internal +public final class CloudWatchJsonFormatter implements JsonFormatter { + private ObjectMapper objectMapper; + + @Override + public String toJsonString(Map m) throws IOException { + if (objectMapper == null) { + objectMapper = ObjectMapper.getDefault(); + } + return objectMapper.writeValueAsString(m); + } + +} diff --git a/aws-cloudwatch-logging/src/main/java/io/micronaut/aws/cloudwatch/logging/CloudWatchLoggingAppender.java b/aws-cloudwatch-logging/src/main/java/io/micronaut/aws/cloudwatch/logging/CloudWatchLoggingAppender.java new file mode 100644 index 0000000000..28a26b4624 --- /dev/null +++ b/aws-cloudwatch-logging/src/main/java/io/micronaut/aws/cloudwatch/logging/CloudWatchLoggingAppender.java @@ -0,0 +1,362 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.aws.cloudwatch.logging; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.AppenderBase; +import ch.qos.logback.core.encoder.Encoder; +import ch.qos.logback.core.net.QueueFactory; +import ch.qos.logback.core.spi.AppenderAttachable; +import ch.qos.logback.core.util.Duration; +import io.micronaut.core.annotation.Internal; +import software.amazon.awssdk.core.exception.SdkException; +import software.amazon.awssdk.services.cloudwatchlogs.model.CreateLogGroupRequest; +import software.amazon.awssdk.services.cloudwatchlogs.model.CreateLogStreamRequest; +import software.amazon.awssdk.services.cloudwatchlogs.model.InputLogEvent; +import software.amazon.awssdk.services.cloudwatchlogs.model.InvalidSequenceTokenException; +import software.amazon.awssdk.services.cloudwatchlogs.model.PutLogEventsRequest; +import software.amazon.awssdk.services.cloudwatchlogs.model.PutLogEventsResponse; +import software.amazon.awssdk.services.cloudwatchlogs.model.ResourceAlreadyExistsException; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.BlockingDeque; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +/** + * Cloudwatch log appender for logback. + * + * @author Nemanja Mikic + * @since 3.9.0 + */ +@Internal +public final class CloudWatchLoggingAppender extends AppenderBase implements AppenderAttachable { + + private static final int DEFAULT_QUEUE_SIZE = 128; + private static final int DEFAULT_MAX_BATCH_SIZE = 128; + private static final int PUT_REQUEST_RETRY_COUNT = 2; + private static final long DEFAULT_PUBLISH_PERIOD = 100; + private final QueueFactory queueFactory = new QueueFactory(); + private Duration eventDelayLimit; + private final List blackListLoggerName = new ArrayList<>(); + private Encoder encoder; + private Future task; + private BlockingDeque deque; + private int queueSize = DEFAULT_QUEUE_SIZE; + private long publishPeriod = DEFAULT_PUBLISH_PERIOD; + private Appender emergencyAppender; + private String sequenceToken = null; + private boolean configuredSuccessfully = false; + private boolean createGroupAndStream = true; + private int maxBatchSize = DEFAULT_MAX_BATCH_SIZE; + private String groupName; + private String streamName; + + public int getQueueSize() { + return queueSize; + } + + public void setQueueSize(int queueSize) { + this.queueSize = queueSize; + } + + public void addBlackListLoggerName(String test) { + this.blackListLoggerName.add(test); + } + + public String getGroupName() { + return groupName; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + public String getStreamName() { + return streamName; + } + + public void setStreamName(String streamName) { + this.streamName = streamName; + } + + public long getPublishPeriod() { + return publishPeriod; + } + + public void setPublishPeriod(long publishPeriod) { + this.publishPeriod = publishPeriod; + } + + public int getMaxBatchSize() { + return maxBatchSize; + } + + public void setMaxBatchSize(int maxBatchSize) { + this.maxBatchSize = maxBatchSize; + } + + public boolean isCreateGroupAndStream() { + return createGroupAndStream; + } + + public void setCreateGroupAndStream(boolean createGroupAndStream) { + this.createGroupAndStream = createGroupAndStream; + } + + @Override + public void start() { + if (isStarted()) { + return; + } + + if (queueSize == 0) { + addWarn("Queue size of zero is deprecated, use a size of one to indicate synchronous processing"); + } + + if (queueSize < 0) { + addError("Queue size must be greater than zero"); + return; + } + + if (publishPeriod <= 0) { + addError("Publish period must be greater than zero"); + return; + } + + if (maxBatchSize <= 0) { + addError("Max Batch size must be greater than zero"); + return; + } + + if (encoder == null) { + addError("No encoder set for the appender named [" + name + "]."); + return; + } + + if (emergencyAppender != null && !emergencyAppender.isStarted()) { + emergencyAppender.start(); + } + + eventDelayLimit = new Duration(publishPeriod); + + deque = queueFactory.newLinkedBlockingDeque(queueSize); + + task = getContext().getScheduledExecutorService().scheduleAtFixedRate(() -> { + try { + dispatchEvents(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + }, 0, 100, TimeUnit.MILLISECONDS); + super.start(); + } + + @Override + public void stop() { + if (!isStarted()) { + return; + } + task.cancel(true); + super.stop(); + } + + @Override + protected void append(ILoggingEvent eventObject) { + if (eventObject == null || !isStarted() || blackListLoggerName.contains(eventObject.getLoggerName())) { + return; + } + + try { + final boolean inserted = deque.offer(eventObject, eventDelayLimit.getMilliseconds(), TimeUnit.MILLISECONDS); + if (!inserted) { + addInfo("Dropping event due to timeout limit of [" + eventDelayLimit + "] being exceeded"); + } + } catch (InterruptedException e) { + addError("Interrupted while appending event to SocketAppender", e); + Thread.currentThread().interrupt(); + } + } + + public Encoder getEncoder() { + return encoder; + } + + public void setEncoder(Encoder encoder) { + this.encoder = encoder; + } + + private boolean tryToConfigure() { + + if (!CloudWatchLoggingClient.isReady()) { + return false; + } + + if (groupName == null) { + groupName = CloudWatchLoggingClient.getAppName(); + } + + if (streamName == null) { + streamName = CloudWatchLoggingClient.getHost(); + } + + if (createGroupAndStream) { + CreateLogGroupRequest createLogGroupRequest = CreateLogGroupRequest.builder().logGroupName(groupName).build(); + try { + CloudWatchLoggingClient.createLogGroup(createLogGroupRequest); + } catch (ResourceAlreadyExistsException e) { + addInfo(String.format("Log group %s already exists", groupName)); + } catch (SdkException e) { + addError(String.format("Error creating log group %s", groupName), e); + } + + CreateLogStreamRequest createLogStreamRequest = CreateLogStreamRequest.builder().logStreamName(streamName).logGroupName(groupName).build(); + try { + CloudWatchLoggingClient.createLogStream(createLogStreamRequest); + } catch (ResourceAlreadyExistsException e) { + addInfo(String.format("Log stream %s already exists", streamName)); + } catch (SdkException e) { + addError(String.format("Error stream log %s", streamName), e); + } + } + + configuredSuccessfully = true; + + return true; + } + + private void dispatchEvents() throws InterruptedException { + if (!configuredSuccessfully && !tryToConfigure()) { + return; + } + + List logEvents = new ArrayList<>(maxBatchSize); + List iLoggingEvents = new ArrayList<>(maxBatchSize); + + while (!deque.isEmpty() && logEvents.size() < maxBatchSize) { + ILoggingEvent event = deque.takeFirst(); + final InputLogEvent inputLogEvent = InputLogEvent.builder().message( + new String(encoder.encode(event)) + ).timestamp(event.getTimeStamp()).build(); + + iLoggingEvents.add(event); + logEvents.add(inputLogEvent); + } + if (!logEvents.isEmpty() && !sendLogsToCloudWatch(logEvents) && emergencyAppender != null) { + iLoggingEvents.forEach(emergencyAppender::doAppend); + } + + } + + private boolean sendLogsToCloudWatch(List logEvents) { + if (sequenceToken == null) { + try { + sequenceToken = CloudWatchLoggingClient.getToken(groupName, streamName); + } catch (SdkException e) { + addError("Getting token got error", e); + } + } + for (int i = 0; i < PUT_REQUEST_RETRY_COUNT; i++) { + try { + PutLogEventsResponse putLogEventsResponse = putLogs(logEvents, groupName, streamName, sequenceToken); + if (putLogEventsResponse == null || putLogEventsResponse.nextSequenceToken() == null) { + addError("Sending log request failed"); + } else { + sequenceToken = putLogEventsResponse.nextSequenceToken(); + return true; + } + } catch (InvalidSequenceTokenException e) { + sequenceToken = e.expectedSequenceToken(); + } catch (Exception e) { + addError("Sending log request failed", e); + return false; + } + } + return false; + } + + @Override + public void addAppender(Appender newAppender) { + if (emergencyAppender == null) { + emergencyAppender = newAppender; + } else { + addWarn("One and only one appender may be attached to " + getClass().getSimpleName()); + addWarn("Ignoring additional appender named [" + newAppender.getName() + "]"); + } + } + + @Override + public Iterator> iteratorForAppenders() { + throw new UnsupportedOperationException("Don't know how to create iterator"); + } + + @Override + public Appender getAppender(String name) { + if (emergencyAppender != null && name != null && name.equals(emergencyAppender.getName())) { + return emergencyAppender; + } else { + return null; + } + } + + @Override + public boolean isAttached(Appender appender) { + return (emergencyAppender == appender); + } + + @Override + public void detachAndStopAllAppenders() { + if (emergencyAppender != null) { + emergencyAppender.stop(); + emergencyAppender = null; + } + } + + @Override + public boolean detachAppender(Appender appender) { + if (emergencyAppender == appender) { + emergencyAppender = null; + return true; + } else { + return false; + } + } + + @Override + public boolean detachAppender(String name) { + if (emergencyAppender != null && emergencyAppender.getName().equals(name)) { + emergencyAppender = null; + return true; + } else { + return false; + } + } + + private PutLogEventsResponse putLogs(List logEvents, + String groupName, + String streamName, + String sequenceToken) { + return CloudWatchLoggingClient.putLogs(PutLogEventsRequest.builder() + .logEvents(logEvents) + .logGroupName(groupName) + .logStreamName(streamName) + .sequenceToken(sequenceToken) + .build()); + } +} diff --git a/aws-cloudwatch-logging/src/main/java/io/micronaut/aws/cloudwatch/logging/CloudWatchLoggingClient.java b/aws-cloudwatch-logging/src/main/java/io/micronaut/aws/cloudwatch/logging/CloudWatchLoggingClient.java new file mode 100644 index 0000000000..6fdf61b8dd --- /dev/null +++ b/aws-cloudwatch-logging/src/main/java/io/micronaut/aws/cloudwatch/logging/CloudWatchLoggingClient.java @@ -0,0 +1,129 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.aws.cloudwatch.logging; + +import io.micronaut.context.annotation.Context; +import io.micronaut.context.event.ApplicationEventListener; +import io.micronaut.core.annotation.Internal; +import io.micronaut.core.annotation.Nullable; +import io.micronaut.discovery.event.ServiceReadyEvent; +import io.micronaut.runtime.ApplicationConfiguration; +import jakarta.annotation.PreDestroy; +import jakarta.inject.Singleton; +import software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsClient; +import software.amazon.awssdk.services.cloudwatchlogs.model.CreateLogGroupRequest; +import software.amazon.awssdk.services.cloudwatchlogs.model.CreateLogStreamRequest; +import software.amazon.awssdk.services.cloudwatchlogs.model.DescribeLogStreamsRequest; +import software.amazon.awssdk.services.cloudwatchlogs.model.LogStream; +import software.amazon.awssdk.services.cloudwatchlogs.model.PutLogEventsRequest; +import software.amazon.awssdk.services.cloudwatchlogs.model.PutLogEventsResponse; + +import java.util.List; +import java.util.Optional; + +/** + * CloudWatchLoggingClient is a {@link CloudWatchLogsClient} client that is required for {@link CloudWatchLoggingAppender}. + * + * @author Nemanja Mikic + * @since 3.9.0 + */ +@Context +@Internal +@Singleton +final class CloudWatchLoggingClient implements ApplicationEventListener { + + private static CloudWatchLogsClient logging; + private static String host; + private static String appName; + private final CloudWatchLogsClient internalLogging; + private final String internalAppName; + + public CloudWatchLoggingClient(CloudWatchLogsClient logging, ApplicationConfiguration applicationConfiguration) { + this.internalLogging = logging; + this.internalAppName = applicationConfiguration.getName().orElse(""); + } + + static synchronized boolean isReady() { + return logging != null; + } + + static synchronized String getHost() { + return host; + } + + static synchronized String getAppName() { + return appName; + } + + private static synchronized void setLogging(CloudWatchLogsClient logging, String host, String appName) { + CloudWatchLoggingClient.logging = logging; + CloudWatchLoggingClient.host = host; + CloudWatchLoggingClient.appName = appName; + } + + static synchronized void destroy() { + CloudWatchLoggingClient.logging.close(); + CloudWatchLoggingClient.logging = null; + CloudWatchLoggingClient.host = null; + CloudWatchLoggingClient.appName = null; + } + + static synchronized PutLogEventsResponse putLogs(PutLogEventsRequest putLogsRequest) { + if (logging != null) { + return logging.putLogEvents(putLogsRequest); + } + return null; + } + + static synchronized void createLogGroup(CreateLogGroupRequest createLogGroupRequest) { + if (logging != null) { + logging.createLogGroup(createLogGroupRequest); + } + } + + static synchronized void createLogStream(CreateLogStreamRequest createLogStreamRequest) { + if (logging != null) { + logging.createLogStream(createLogStreamRequest); + } + } + + @Nullable + static synchronized String getToken(String groupName, String streamName) { + List logStreams = logging.describeLogStreams( + DescribeLogStreamsRequest.builder() + .logGroupName(groupName) + .logStreamNamePrefix(streamName) + .build()).logStreams(); + if (!logStreams.isEmpty()) { + Optional first = logStreams.stream().filter(x -> x.logStreamName().equals(streamName)).findFirst(); + if (first.isPresent()) { + return first.get().uploadSequenceToken(); + } + } + return null; + } + + @PreDestroy + public void close() { + CloudWatchLoggingClient.destroy(); + } + + @Override + public void onApplicationEvent(ServiceReadyEvent event) { + setLogging(internalLogging, event.getSource().getHost(), internalAppName); + } + +} diff --git a/aws-cloudwatch-logging/src/main/java/io/micronaut/aws/cloudwatch/logging/package-info.java b/aws-cloudwatch-logging/src/main/java/io/micronaut/aws/cloudwatch/logging/package-info.java new file mode 100644 index 0000000000..6f6d34129f --- /dev/null +++ b/aws-cloudwatch-logging/src/main/java/io/micronaut/aws/cloudwatch/logging/package-info.java @@ -0,0 +1,22 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://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. + */ +/** + * Integration with AWS CloudWatch logging. + * + * @author Nemanja Mikic + * @since 3.9.0 + */ +package io.micronaut.aws.cloudwatch.logging; diff --git a/aws-cloudwatch-logging/src/test/groovy/io/micronaut/aws/cloudwatch/logging/CloudWatchLoggingAppenderSpec.groovy b/aws-cloudwatch-logging/src/test/groovy/io/micronaut/aws/cloudwatch/logging/CloudWatchLoggingAppenderSpec.groovy new file mode 100644 index 0000000000..696b643fe2 --- /dev/null +++ b/aws-cloudwatch-logging/src/test/groovy/io/micronaut/aws/cloudwatch/logging/CloudWatchLoggingAppenderSpec.groovy @@ -0,0 +1,282 @@ +package io.micronaut.aws.cloudwatch.logging + +import ch.qos.logback.classic.Level +import ch.qos.logback.classic.LoggerContext +import ch.qos.logback.classic.PatternLayout +import ch.qos.logback.classic.spi.LoggingEvent +import ch.qos.logback.core.encoder.LayoutWrappingEncoder +import io.micronaut.discovery.ServiceInstance +import io.micronaut.discovery.event.ServiceReadyEvent +import io.micronaut.runtime.ApplicationConfiguration +import spock.lang.Specification +import spock.util.concurrent.PollingConditions + +class CloudWatchLoggingAppenderSpec extends Specification { + + CloudWatchLoggingAppender appender + LoggerContext context + PatternLayout layout + LayoutWrappingEncoder encoder + CloudwatchLoggingSpec.MockLogging cloudWatchLogsClient + + def setup() { + context = new LoggerContext() + layout = new PatternLayout() + layout.context = context + layout.pattern = "[%thread] %level %logger{20} - %msg%n%xThrowable" + layout.start() + encoder = new LayoutWrappingEncoder() + encoder.layout = layout + encoder.start() + appender = new CloudWatchLoggingAppender() + appender.context = context + appender.encoder = encoder + def config = Stub(ApplicationConfiguration) { + getName() >> Optional.of("my-awesome-app") + } + def instance = Mock(ServiceInstance.class) + instance.getHost() >> "testHost" + def serviceReadyEvent = new ServiceReadyEvent(instance) + + cloudWatchLogsClient = new CloudwatchLoggingSpec.MockLogging() + + new CloudWatchLoggingClient(cloudWatchLogsClient, config).onApplicationEvent(serviceReadyEvent) + + } + + def cleanup() { + layout.stop() + encoder.stop() + appender.stop() + CloudWatchLoggingClient.destroy() + } + + void 'test error queue size less then 0'() { + when: + appender.queueSize = -1 + appender.start() + + then: + def statuses = context.getStatusManager().getCopyOfStatusList() + statuses.find { it.message == "Queue size must be greater than zero" } + } + + void 'test error queue size equal to 0'() { + when: + appender.queueSize = 0 + appender.start() + + then: + def statuses = context.getStatusManager().getCopyOfStatusList() + statuses.find { it.message == "Queue size of zero is deprecated, use a size of one to indicate synchronous processing" } + } + + void 'test error publish period less or equal to 0'() { + when: + appender.queueSize = 100 + appender.publishPeriod = 0 + appender.start() + + then: + def statuses = context.getStatusManager().getCopyOfStatusList() + statuses.find { it.message == "Publish period must be greater than zero" } + } + + void 'test error max batch size less or equal to 0'() { + when: + appender.maxBatchSize = 0 + appender.start() + + then: + def statuses = context.getStatusManager().getCopyOfStatusList() + statuses.find { it.message == "Max Batch size must be greater than zero" } + } + + void 'encoder not set'() { + when: + appender.queueSize = 100 + appender.publishPeriod = 100 + appender.encoder = null + appender.start() + + then: + def statuses = context.getStatusManager().getCopyOfStatusList() + statuses.find { it.message == "No encoder set for the appender named [null]." } + } + + void 'register multiple emergency appender'() { + when: + def mockAppender = new MockAppender() + appender.queueSize = 100 + appender.publishPeriod = 100 + appender.encoder = new LayoutWrappingEncoder() + appender.addAppender(mockAppender) + appender.addAppender(mockAppender) + + then: + def statuses = context.getStatusManager().getCopyOfStatusList() + statuses.find { it.message == "One and only one appender may be attached to CloudWatchLoggingAppender" } + statuses.find { it.message == "Ignoring additional appender named [MockAppender]" } + appender.getAppender("MockAppender") != null + appender.getAppender("NotExistingOne") == null + appender.isAttached(mockAppender) + appender.encoder != null + appender.queueSize == 100 + appender.publishPeriod == 100 + + appender.detachAndStopAllAppenders() + !appender.isAttached(mockAppender) + } + + void 'detach emergency appender by name'() { + when: + def mockAppender = new MockAppender() + appender.queueSize = 100 + appender.publishPeriod = 100 + appender.encoder = new LayoutWrappingEncoder() + appender.addAppender(mockAppender) + + then: + appender.detachAppender("MockAppender") + !appender.detachAppender("NotExistingOne") + } + + void 'detach emergency appender by instance'() { + when: + def mockAppender = new MockAppender() + appender.queueSize = 100 + appender.publishPeriod = 100 + appender.encoder = new LayoutWrappingEncoder() + appender.addAppender(mockAppender) + + then: + appender.detachAppender(mockAppender) + !appender.detachAppender(mockAppender) + } + + void 'try to create iterator for emergency appender'() { + when: + def mockAppender = new MockAppender() + appender.queueSize = 100 + appender.publishPeriod = 100 + appender.encoder = new LayoutWrappingEncoder() + appender.addAppender(mockAppender) + appender.iteratorForAppenders() + + then: + thrown(UnsupportedOperationException) + } + + void 'custom groupName and StreamName'() { + given: + def testGroup = "testGroup" + def testStream = "testStream" + def testMessage = "testMessage" + PollingConditions conditions = new PollingConditions(timeout: 10, initialDelay: 1.5, factor: 1.25) + LoggingEvent event = createEvent("name", Level.INFO, testMessage, System.currentTimeMillis()) + + when: + appender.groupName = testGroup + appender.streamName = testStream + appender.start() + appender.doAppend(event) + + then: + appender.groupName == testGroup + appender.streamName == testStream + conditions.eventually { + cloudWatchLogsClient.putLogsRequestList.size() == 1 + } + cloudWatchLogsClient.putLogsRequestList.get(0).logGroupName() == testGroup + cloudWatchLogsClient.putLogsRequestList.get(0).logStreamName() == testStream + + } + + void 'test create group and stream flag'() { + given: + def testMessage = "testMessage" + PollingConditions conditions = new PollingConditions(timeout: 10, initialDelay: 1.5, factor: 1.25) + LoggingEvent event = createEvent("name", Level.INFO, testMessage, System.currentTimeMillis()) + + when: + appender.maxBatchSize = 10 + appender.createGroupAndStream = false + appender.groupName = "test" + appender.streamName = "test" + cloudWatchLogsClient.resetCalls() + appender.start() + appender.doAppend(event) + + then: + appender.maxBatchSize == 10 + !appender.isCreateGroupAndStream() + conditions.eventually { + cloudWatchLogsClient.putLogsRequestList.size() == 1 + } + cloudWatchLogsClient.numberOfCalls.isEmpty() + } + + void 'test resource already exists when creating group and stream'() { + given: + def testMessage = "testMessage2" + def testGroup = "testGroup" + def testStream = "testStream" + PollingConditions conditions = new PollingConditions(timeout: 10, initialDelay: 1.5, factor: 1.25) + LoggingEvent event = createEvent("name", Level.INFO, testMessage, System.currentTimeMillis()) + + when: + appender.groupName = testGroup + appender.streamName = testStream + cloudWatchLogsClient.state = CloudwatchLoggingSpec.MockState.NOT_SUCCESSFUL + appender.start() + appender.doAppend(event) + + then: + conditions.eventually { + cloudWatchLogsClient.putLogsRequestList.size() == 2 + } + def statuses = context.getStatusManager().getCopyOfStatusList() + statuses.find { it.message == "Log group " + testGroup + " already exists" } + statuses.find { it.message == "Log stream " + testStream + " already exists" } + statuses.find { it.message == "Sending log request failed" } + } + + void 'test exception handling'() { + given: + def testMessage = "testMessage2" + def testGroup = "testGroup" + def testStream = "testStream" + + PollingConditions conditions = new PollingConditions(timeout: 10, initialDelay: 1.5, factor: 1.25) + LoggingEvent event = createEvent("name", Level.INFO, testMessage, System.currentTimeMillis()) + + when: + appender.groupName = testGroup + appender.streamName = testStream + cloudWatchLogsClient.state = CloudwatchLoggingSpec.MockState.EXCEPTION + appender.start() + appender.doAppend(event) + + then: + conditions.eventually { + cloudWatchLogsClient.putLogsRequestList.size() == 1 + } + def statuses = context.getStatusManager().getCopyOfStatusList() + statuses.find { it.message == "Error creating log group " + testGroup } + statuses.find { it.message == "Error stream log " + testStream } + statuses.find { it.message == "Sending log request failed" } + statuses.findAll { it.throwable != null }.size() == 3 + } + + LoggingEvent createEvent(String name, Level level, String message, Long time) { + LoggingEvent event = new LoggingEvent() + event.loggerName = name + event.level = level + event.message = message + if (time != null) { + event.timeStamp = time + } + return event + } + +} diff --git a/aws-cloudwatch-logging/src/test/groovy/io/micronaut/aws/cloudwatch/logging/CloudwatchLoggingSpec.groovy b/aws-cloudwatch-logging/src/test/groovy/io/micronaut/aws/cloudwatch/logging/CloudwatchLoggingSpec.groovy new file mode 100644 index 0000000000..54ff77902f --- /dev/null +++ b/aws-cloudwatch-logging/src/test/groovy/io/micronaut/aws/cloudwatch/logging/CloudwatchLoggingSpec.groovy @@ -0,0 +1,181 @@ +package io.micronaut.aws.cloudwatch.logging + +import io.micronaut.context.annotation.Property +import io.micronaut.context.annotation.Replaces +import io.micronaut.context.annotation.Requires +import io.micronaut.context.event.ApplicationEventPublisher +import io.micronaut.discovery.ServiceInstance +import io.micronaut.discovery.event.ServiceReadyEvent +import io.micronaut.runtime.ApplicationConfiguration +import io.micronaut.serde.ObjectMapper +import io.micronaut.test.extensions.spock.annotation.MicronautTest +import jakarta.inject.Inject +import jakarta.inject.Singleton +import org.slf4j.LoggerFactory +import software.amazon.awssdk.awscore.exception.AwsServiceException +import software.amazon.awssdk.core.exception.SdkClientException +import software.amazon.awssdk.core.exception.SdkException +import software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsClient +import software.amazon.awssdk.services.cloudwatchlogs.model.* +import spock.lang.Specification +import spock.util.concurrent.PollingConditions + +@Property(name = "spec.name", value = "CloudwatchLoggingSpec") +@MicronautTest +class CloudwatchLoggingSpec extends Specification { + + @Inject + CloudWatchLogsClient logging + + @Inject + ApplicationEventPublisher eventPublisher + + @Inject + ApplicationConfiguration applicationConfiguration + + void "test Cloudwatch logging"() { + given: + def logMessage = 'test logging' + def testHost = 'testHost' + def logger = LoggerFactory.getLogger(CloudwatchLoggingSpec.class) + PollingConditions conditions = new PollingConditions(timeout: 10, initialDelay: 1.5, factor: 1.25) + + def mockLogging = (MockLogging) logging + def instance = Mock(ServiceInstance.class) + instance.getHost() >> testHost + def event = new ServiceReadyEvent(instance) + eventPublisher.publishEvent(event) + + when: + logger.info(logMessage) + + then: + conditions.eventually { + mockLogging.getPutLogsRequestList().size() != 0 + } + + def putLogRequestList = ((MockLogging) logging).getPutLogsRequestList() + putLogRequestList.stream().allMatch(x -> x.logGroupName() == applicationConfiguration.getName().get()) + putLogRequestList.stream().allMatch(x -> x.logStreamName() == testHost) + + ObjectMapper mapper = ObjectMapper.getDefault() + + def logEntries = new ArrayList>() + + putLogRequestList.forEach( + x -> { + x.logEvents().stream().forEach(y -> logEntries.add( + mapper.readValue(y.message(), HashMap.class) as Map + )) + } + ) + + logEntries.stream().anyMatch(x -> x.logger == 'io.micronaut.context.env.DefaultEnvironment') + logEntries.stream().anyMatch(x -> x.logger == 'io.micronaut.aws.cloudwatch.logging.CloudwatchLoggingSpec') + logEntries.stream().anyMatch(x -> x.message == logMessage) + MockAppender.getEvents().size() == 0 + + when: + mockLogging.state = MockState.NOT_SUCCESSFUL + logger.info(logMessage) + conditions = new PollingConditions(timeout: 10, initialDelay: 1.5, factor: 1.25) + + then: + conditions.eventually { + MockAppender.getEvents().size() == 1 + } + MockAppender.getEvents().get(0).message == logMessage + + when: + mockLogging.state = MockState.EXCEPTION + logger.info(logMessage + " from Exception") + conditions = new PollingConditions(timeout: 10, initialDelay: 1.5, factor: 1.25) + + then: + conditions.eventually { + MockAppender.getEvents().size() == 2 + } + MockAppender.getEvents().get(1).message == logMessage + " from Exception" + } + + static enum MockState { + SUCCESS, + NOT_SUCCESSFUL, + EXCEPTION + } + + @Requires(property = "spec.name", value = "CloudwatchLoggingSpec") + @Singleton + @Replaces(CloudWatchLogsClient) + static class MockLogging implements CloudWatchLogsClient { + + final List putLogsRequestList = Collections.synchronizedList(new ArrayList<>()) + + MockState state = MockState.SUCCESS + + Map numberOfCalls = new HashMap<>() + + @Override + CreateLogGroupResponse createLogGroup(CreateLogGroupRequest createLogGroupRequest) throws InvalidParameterException, ResourceAlreadyExistsException, LimitExceededException, OperationAbortedException, ServiceUnavailableException, AwsServiceException, SdkClientException, CloudWatchLogsException { + incrementVisit("createLogGroup") + if (state == MockState.SUCCESS) { + return CreateLogGroupResponse.builder().build() as CreateLogGroupResponse + } else if (state == MockState.NOT_SUCCESSFUL) { + throw ResourceAlreadyExistsException.builder().build() + } + throw SdkException.builder().message("testMessage").build() + } + + @Override + CreateLogStreamResponse createLogStream(CreateLogStreamRequest createLogStreamRequest) throws InvalidParameterException, ResourceAlreadyExistsException, ResourceNotFoundException, ServiceUnavailableException, AwsServiceException, SdkClientException, CloudWatchLogsException { + incrementVisit("createLogStream") + if (state == MockState.SUCCESS) { + return CreateLogStreamResponse.builder().build() as CreateLogStreamResponse + } else if (state == MockState.NOT_SUCCESSFUL) { + throw ResourceAlreadyExistsException.builder().build() + } + throw SdkException.builder().message("testMessage").build() + } + + @Override + DescribeLogStreamsResponse describeLogStreams(DescribeLogStreamsRequest describeLogStreamsRequest) throws InvalidParameterException, ResourceNotFoundException, ServiceUnavailableException, AwsServiceException, SdkClientException, CloudWatchLogsException { + if (state == MockState.SUCCESS) { + return DescribeLogStreamsResponse.builder() + .logStreams(LogStream.builder().uploadSequenceToken("dummyToken") + .logStreamName(describeLogStreamsRequest.logStreamNamePrefix()).build()) + .build() as DescribeLogStreamsResponse + } + return DescribeLogStreamsResponse.builder().build() as DescribeLogStreamsResponse + } + + @Override + PutLogEventsResponse putLogEvents(PutLogEventsRequest putLogEventsRequest) throws InvalidParameterException, InvalidSequenceTokenException, DataAlreadyAcceptedException, ResourceNotFoundException, ServiceUnavailableException, UnrecognizedClientException, AwsServiceException, SdkClientException, CloudWatchLogsException { + putLogsRequestList.add(putLogEventsRequest) + if (state == MockState.SUCCESS) { + return PutLogEventsResponse.builder().nextSequenceToken("nextSeqToken").build() as PutLogEventsResponse + } else if (state == MockState.NOT_SUCCESSFUL) { + return PutLogEventsResponse.builder().build() as PutLogEventsResponse + } else { + throw SdkException.builder().message("testMessage").build() + } + } + + @Override + void close() { + + } + + @Override + String serviceName() { + return null + } + + void resetCalls() { + numberOfCalls = new HashMap<>() + } + + void incrementVisit(String methodName) { + numberOfCalls.compute(methodName, (k, v) -> (v == null) ? 1 : v + 1) + } + } +} diff --git a/aws-cloudwatch-logging/src/test/java/io/micronaut/aws/cloudwatch/logging/MockAppender.java b/aws-cloudwatch-logging/src/test/java/io/micronaut/aws/cloudwatch/logging/MockAppender.java new file mode 100644 index 0000000000..717006a88f --- /dev/null +++ b/aws-cloudwatch-logging/src/test/java/io/micronaut/aws/cloudwatch/logging/MockAppender.java @@ -0,0 +1,30 @@ +package io.micronaut.aws.cloudwatch.logging; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; + +import java.util.ArrayList; +import java.util.List; + +public class MockAppender extends AppenderBase { + + private static final List events = new ArrayList<>(); + + static List getEvents() { + synchronized (events) { + return new ArrayList<>(events); + } + } + + @Override + public String getName() { + return "MockAppender"; + } + + @Override + protected void append(ILoggingEvent eventObject) { + synchronized (events) { + events.add(eventObject); + } + } +} diff --git a/aws-cloudwatch-logging/src/test/resources/application.yml b/aws-cloudwatch-logging/src/test/resources/application.yml new file mode 100644 index 0000000000..9039104907 --- /dev/null +++ b/aws-cloudwatch-logging/src/test/resources/application.yml @@ -0,0 +1,3 @@ +micronaut: + application: + name: testapp diff --git a/aws-cloudwatch-logging/src/test/resources/logback.xml b/aws-cloudwatch-logging/src/test/resources/logback.xml new file mode 100644 index 0000000000..775ac60582 --- /dev/null +++ b/aws-cloudwatch-logging/src/test/resources/logback.xml @@ -0,0 +1,23 @@ + + + + + + + + name1 + name2 + 300 + 120 + + + + + + + + + + + diff --git a/aws-sdk-v2/build.gradle.kts b/aws-sdk-v2/build.gradle.kts index 311bb56a36..6480579d15 100644 --- a/aws-sdk-v2/build.gradle.kts +++ b/aws-sdk-v2/build.gradle.kts @@ -23,9 +23,11 @@ dependencies { compileOnly(libs.awssdk.ssm) compileOnly(libs.awssdk.secretsmanager) compileOnly(libs.awssdk.servicediscovery) + compileOnly(libs.awssdk.cloudwatchlogs) // Tests testAnnotationProcessor(mn.micronaut.inject.java) + testImplementation(libs.awssdk.cloudwatchlogs) testImplementation(libs.awssdk.apigatewaymanagementapi) testImplementation(libs.awssdk.servicediscovery) testImplementation(libs.awssdk.url.connection.client) diff --git a/aws-sdk-v2/src/main/java/io/micronaut/aws/sdk/v2/service/cloudwatchlogs/CloudwatchLogsClientFactory.java b/aws-sdk-v2/src/main/java/io/micronaut/aws/sdk/v2/service/cloudwatchlogs/CloudwatchLogsClientFactory.java new file mode 100644 index 0000000000..d85b08edb3 --- /dev/null +++ b/aws-sdk-v2/src/main/java/io/micronaut/aws/sdk/v2/service/cloudwatchlogs/CloudwatchLogsClientFactory.java @@ -0,0 +1,90 @@ +/* + * Copyright 2017-2021 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.aws.sdk.v2.service.cloudwatchlogs; + +import io.micronaut.aws.sdk.v2.service.AwsClientFactory; +import io.micronaut.context.annotation.Bean; +import io.micronaut.context.annotation.BootstrapContextCompatible; +import io.micronaut.context.annotation.Factory; +import io.micronaut.context.annotation.Requires; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProviderChain; +import software.amazon.awssdk.http.SdkHttpClient; +import software.amazon.awssdk.http.async.SdkAsyncHttpClient; +import software.amazon.awssdk.regions.providers.AwsRegionProviderChain; +import software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsAsyncClient; +import software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsAsyncClientBuilder; +import software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsClient; +import software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsClientBuilder; + +import jakarta.inject.Singleton; + +/** + * Factory that creates a CloudWatch Logs client. + * @author Nemanja Mikic + * @since 2.6.0 + */ +@Factory +@BootstrapContextCompatible +public class CloudwatchLogsClientFactory extends AwsClientFactory { + + /** + * Constructor. + * + * @param credentialsProvider The credentials provider + * @param regionProvider The region provider + */ + protected CloudwatchLogsClientFactory(AwsCredentialsProviderChain credentialsProvider, AwsRegionProviderChain regionProvider) { + super(credentialsProvider, regionProvider); + } + + @Override + protected CloudWatchLogsClientBuilder createSyncBuilder() { + return CloudWatchLogsClient.builder(); + } + + @Override + protected CloudWatchLogsAsyncClientBuilder createAsyncBuilder() { + return CloudWatchLogsAsyncClient.builder(); + } + + @Override + @Singleton + public CloudWatchLogsClientBuilder syncBuilder(SdkHttpClient httpClient) { + return super.syncBuilder(httpClient); + } + + @Override + @Bean(preDestroy = "close") + @Singleton + public CloudWatchLogsClient syncClient(CloudWatchLogsClientBuilder builder) { + return super.syncClient(builder); + } + + @Override + @Singleton + @Requires(beans = SdkAsyncHttpClient.class) + public CloudWatchLogsAsyncClientBuilder asyncBuilder(SdkAsyncHttpClient httpClient) { + return super.asyncBuilder(httpClient); + } + + @Override + @Bean(preDestroy = "close") + @Singleton + @Requires(beans = SdkAsyncHttpClient.class) + public CloudWatchLogsAsyncClient asyncClient(CloudWatchLogsAsyncClientBuilder builder) { + return super.asyncClient(builder); + } +} diff --git a/aws-sdk-v2/src/test/groovy/io/micronaut/aws/sdk/v2/service/CloudWatchLogsClientSpec.groovy b/aws-sdk-v2/src/test/groovy/io/micronaut/aws/sdk/v2/service/CloudWatchLogsClientSpec.groovy new file mode 100644 index 0000000000..64d63e6485 --- /dev/null +++ b/aws-sdk-v2/src/test/groovy/io/micronaut/aws/sdk/v2/service/CloudWatchLogsClientSpec.groovy @@ -0,0 +1,24 @@ +package io.micronaut.aws.sdk.v2.service + +import io.micronaut.aws.sdk.v2.ApplicationContextSpecification +import software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsAsyncClient +import software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsClient + +class CloudWatchLogsClientSpec extends ApplicationContextSpecification { + + void "it can configure a sync client"() { + when: + CloudWatchLogsClient client = applicationContext.getBean(CloudWatchLogsClient) + + then: + client.serviceName() == CloudWatchLogsClient.SERVICE_NAME + } + + void "it can configure an async client"() { + when: + CloudWatchLogsAsyncClient client = applicationContext.getBean(CloudWatchLogsAsyncClient) + + then: + client.serviceName() == CloudWatchLogsClient.SERVICE_NAME + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b6ece5e59a..e93189dcdc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,6 +2,7 @@ bouncycastle = '1.70' fileupload = '0.0.5' jetty = '9.4.48.v20220622' +logback-json-classic = '0.1.5' managed-alexa-ask-sdk = "2.44.0" managed-aws-java-sdk-v1 = '1.12.296' managed-aws-java-sdk-v2 = '2.17.271' @@ -22,6 +23,7 @@ alexa-ask-sdk-apache-client = { module = 'com.amazon.alexa:ask-sdk-apache-client aws-ssm = { module = 'software.amazon.awssdk:ssm' } awssdk-apache-client = { module = 'software.amazon.awssdk:apache-client' } awssdk-apigatewaymanagementapi = { module = 'software.amazon.awssdk:apigatewaymanagementapi' } +awssdk-cloudwatchlogs = { module = 'software.amazon.awssdk:cloudwatchlogs'} awssdk-dynamodb = { module = 'software.amazon.awssdk:dynamodb' } awssdk-netty-nio-client = { module = 'software.amazon.awssdk:netty-nio-client' } awssdk-rekognition = { module = 'software.amazon.awssdk:rekognition' } @@ -45,6 +47,8 @@ jackson-afterburner = { module = 'com.fasterxml.jackson.module:jackson-module-af jetty-server = { module = 'org.eclipse.jetty:jetty-server', version.ref = 'jetty' } jcl-over-slf4j = { module = 'org.slf4j:jcl-over-slf4j', version.ref = 'slf4j' } jupiter-engine = { module = 'org.junit.jupiter:junit-jupiter-engine' } +logback-json-classic = { module = 'ch.qos.logback.contrib:logback-json-classic', version.ref = 'logback-json-classic' } +jackson-databind = { module = 'com.fasterxml.jackson.core:jackson-databind' } managed-alexa-ask-sdk-core = { module = 'com.amazon.alexa:ask-sdk-core', version.ref = 'managed-alexa-ask-sdk' } managed-alexa-ask-sdk-lambda = { module = 'com.amazon.alexa:ask-sdk-lambda-support', version.ref = 'managed-alexa-ask-sdk' } diff --git a/settings.gradle.kts b/settings.gradle.kts index 64be1a47a4..c0e72920e7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -28,6 +28,7 @@ include("aws-common") include("aws-sdk-v1") include("aws-sdk-v2") include("aws-cdk") +include("aws-cloudwatch-logging") include("function-aws-test") include("test-suite") include("test-suite-groovy") diff --git a/src/main/docs/guide/sdkv2/cloudWatchLogging.adoc b/src/main/docs/guide/sdkv2/cloudWatchLogging.adoc new file mode 100644 index 0000000000..7014c71119 --- /dev/null +++ b/src/main/docs/guide/sdkv2/cloudWatchLogging.adoc @@ -0,0 +1,17 @@ +NOTE: This does not apply to AWS Lambda. AWS Lambda publishes automatically to CloudWatch logs produced to standard out. + +To use the https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/WhatIsCloudWatchLogs.html[Cloudwatch Logs], add the following dependency to your project: + +dependency:io.micronaut.aws:micronaut-aws-cloudwatch-logging[] + +Then, the following beans will be created: + +* `software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsClient` +* `software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsClientBuilder` + +And: + +* `software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsAsyncClient` +* `software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsAsyncClientBuilder` + +The HTTP client, credentials and region will be configured as per described in the <>. diff --git a/src/main/docs/guide/sdkv2/cloudWatchLogging/browsingTheLogs.adoc b/src/main/docs/guide/sdkv2/cloudWatchLogging/browsingTheLogs.adoc new file mode 100644 index 0000000000..b86faa86c9 --- /dev/null +++ b/src/main/docs/guide/sdkv2/cloudWatchLogging/browsingTheLogs.adoc @@ -0,0 +1,3 @@ +When you have completed a setup, you can browse your logs on the https://console.aws.amazon.com/cloudwatch/home[Cloudwatch home]. In the Log groups menu and choose the group name and stream name of your application. In the "Log events" section you should be able to see your service logs. + +image::logs.png[Log events] diff --git a/src/main/docs/guide/sdkv2/cloudWatchLogging/emergencyAppender.adoc b/src/main/docs/guide/sdkv2/cloudWatchLogging/emergencyAppender.adoc new file mode 100644 index 0000000000..2cfb716dbb --- /dev/null +++ b/src/main/docs/guide/sdkv2/cloudWatchLogging/emergencyAppender.adoc @@ -0,0 +1,35 @@ +Since this appender is queuing up log messages and then writing them remotely, there are a number of situations which might result in log messages not getting remoted correctly. To address such scenarios you can configure the emergency appender to preserve those messages. + +Inside your `src/main/resources/logback.xml` you should add a new appender, in the example it is `STDOUT`. Inside the `CloudWatchLoggingAppender` add `appender-ref` that points to the new crated appender. + +.src/main/resources/logback.xml +[source,xml] +---- + + + true + + + %cyan(%d{HH:mm:ss.SSS}) %gray([%thread]) %highlight(%-5level) %magenta(%logger{36}) - %msg%n + + + + + + + + + + + + + + + + + + + + +---- diff --git a/src/main/docs/guide/sdkv2/cloudWatchLogging/logbackConfiguration.adoc b/src/main/docs/guide/sdkv2/cloudWatchLogging/logbackConfiguration.adoc new file mode 100644 index 0000000000..5362cc14ad --- /dev/null +++ b/src/main/docs/guide/sdkv2/cloudWatchLogging/logbackConfiguration.adoc @@ -0,0 +1,28 @@ + +Edit a `src/main/resources/logback.xml` file and make it look like this: + +.src/main/resources/logback.xml +[source,xml] +---- + + + + + + + + + + + + + + + + + + +---- +You can customize your JsonLayout with additional parameters the are available on official docs of https://javadoc.io/static/ch.qos.logback.contrib/logback-json-classic/0.1.5/ch/qos/logback/contrib/json/classic/JsonLayout.html[Logback's JsonLayout]. + +The `CloudWatchLoggingAppender` supports blacklisting the loggers by specifying the logger name. That might come handy if you want to use `level=DEBUG` or `level=TRACE` for the root logger level. diff --git a/src/main/docs/guide/sdkv2/cloudWatchLogging/openTelemetryAndLogging.adoc b/src/main/docs/guide/sdkv2/cloudWatchLogging/openTelemetryAndLogging.adoc new file mode 100644 index 0000000000..ef3b7062ba --- /dev/null +++ b/src/main/docs/guide/sdkv2/cloudWatchLogging/openTelemetryAndLogging.adoc @@ -0,0 +1,32 @@ +If you are using the https://opentelemetry.io/[OpenTelemetry] for tracing you can include `traceId` and `spanId` fields into your logs. First you have to add next dependency into your project: + +dependency:io.opentelemetry.instrumentation:opentelemetry-logback-mdc-1.0:1.16.0-alpha[scope="runtime"] + +Inside your `src/main/resources/logback.xml` you should add a new appender https://javadoc.io/doc/io.opentelemetry.instrumentation/opentelemetry-logback-1.0/latest/io/opentelemetry/instrumentation/logback/v1_0/OpenTelemetryAppender.html[io.opentelemetry.instrumentation.logback.v1_0.OpenTelemetryAppender]. + +.src/main/resources/logback.xml +[source,xml] +---- + + + + + + + + + + + + + + + + + + + + + + +---- diff --git a/src/main/docs/guide/sdkv2/cloudWatchLogging/setCloudWatchLogging.adoc b/src/main/docs/guide/sdkv2/cloudWatchLogging/setCloudWatchLogging.adoc new file mode 100644 index 0000000000..03af42a203 --- /dev/null +++ b/src/main/docs/guide/sdkv2/cloudWatchLogging/setCloudWatchLogging.adoc @@ -0,0 +1,42 @@ +By default, Micronaut application will try to create the group and stream for you using AWS client. To disable this behavior you have to set the `createGroupAndStream` flag to `false` inside your appender. +Default value for group name is your application name and for stream name is your hostname. If you want you can change them by setting value `groupName` for group name and `streamName` for stream name. + +.Configurable CloudWatchLoggingAppender Appender Properties +|=== +|Property|Type|Default value|Description + +|`groupName` +|`String` +|Application name +|Cloudwatch Log group name + +|`streamName` +|`String` +|Host name +|Cloudwatch Log stream name + +|`publishPeriod` +|`Integer` +|100 +|Time in ms between two batch publishing of logs + +|`maxBatchSize` +|`Integer` +|128 +|Time maximum number of log lines that will be sent in one batch request + +|`queueSize` +|`Integer` +|128 +|The size of publishing log queue + +|`createGroupAndStream` +|`Boolean` +|true +|If flag is set to true the Micronaut application will try to create group and stream on the AWS + +|`blackListLoggerName` +|`List` +|empty +|List of logger names that won't be published +|=== diff --git a/src/main/docs/guide/toc.yml b/src/main/docs/guide/toc.yml index 178f7eb5a1..ed23508a97 100644 --- a/src/main/docs/guide/toc.yml +++ b/src/main/docs/guide/toc.yml @@ -29,6 +29,13 @@ sdkv2: ssm: SSM secretsmanager: Secrets Manager servicediscovery: Service discovery + cloudWatchLogging: + title: CloudWatch logging + setCloudWatchLogging: Set CloudWatch Logging + logbackConfiguration: Logback configuration + openTelemetryAndLogging: OpenTelemetry and logging + emergencyAppender: Emergency Appender + browsingTheLogs: Browsing the Logs advancedConfiguration: Advanced configuration otherServices: Other services lambda: diff --git a/src/main/docs/resources/img/logs.png b/src/main/docs/resources/img/logs.png new file mode 100644 index 0000000000000000000000000000000000000000..7bfd5080ca4df14928519e53ae771bcf3b57351d GIT binary patch literal 303540 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

&eSA9Q2F6Avcu$vdy;PT9N*FI$Q z?x$;Pv_Z>sc@B8&P0<4mH~JzJ*8u>Eoa0ExlIHvaNugI%*{^o3Su$-w8@CuD6S|1C z38x~l0C?I*$2Mti3UFHW1YjOg`Amh5M73dsl=x2GF+p;LpfJ>-WXb?(YLGM65K}%x zQkw>73`ha&pGW$JAutpg2LpNkYEJa{H_rj$O+65^KR=##Q3rTXvlVd5K3ry_=GgD+ z^`37$k5+Vs7y)h^Ci#jD!;Qteg<-JnALusY!3hwt6qi(ym7)bPKb_MuG4QN&B<3y8aQ96YW_+0F~UHWMk^E3X^<^9!7Z<}%ap<5^> z_?Lc{{VEUEE67`e`Hr*Yy2W2m&_fqwazstrY<^E&yS@wnUv&0Vrm-5R!U8dwp3?qJ zQsxwDmnCT{JFb@Ut?fO5XoN3^v`P*~Ga5V)Yk|9_S3tH(bxWAX2D59vbro>${AK(q z65U_!Ywe{30`NG(iDJ9S@|nNm{~vGk%)+Uj@99ANi=08C~`FMtAmYgrtd!jdk+=d=>3 zn8sRNRsN%D6|0!QDRA9+x%s`X=}$^EEUa?fs%X3PS1duziya*utg>JUzzV#8DRz*M zlHVC~FLnev{D!lZHT-&C(J*mGIR7nJVr4`Z4@1z+BgY(k0HjU&HDPNeDAT`hlfH#)U( zIyK}Q8e=Q*(OW4RGit__unFi~zdcn!!(*7t8D>3=^i|AZC24YA)w~xqxP`p9xE8Da z#57I0qf3el~8gdt9{(N=VN5Ch;e-KFHx#;7~Isz$rIk3+c3%*;~ssZxek3I$RI z=!;TC*b-9Ojb+ldg+Ej@8`Fm`awm-4b6WSa{Bv8EwGMe|=G`ZNhk!>%eJ9-ZME7~t z6N}2htF4wL6gJuwUTwzn#i=WEO%XwB)Rz;YPoc&hn<&9B)XQJQd&el~S7kS+OXW}{ z3O?}+x`vOS{9MpM0$0xOyGoPgV_u$}U68#pzaE=NtZUX`1Lt1r?%o@Ev#x{PzU0kx zMev4jJo@oKi}Nb8OFT#^!HlrXFMi)5ESkVWV!mtjI!VO)j5GEbSbBid3pwhBwt9lM zUF=&O<1qH=GfTRi)_{9Aw|)}7-TpUA6*g8o&qew*ePr2<8|5Qd3LYyeyymMM(j{|a zVUiNDbtp@$M=y?;myV&)+rInRh5eYXHJd47c}IPax2I%Zzd?OyQ4`OgF$?UJOlTh! zEby#I(=3qwM25pK`E*#4dT}FyH`~dsYZ=qG7~f~I$TIeC-Q#)Nz`Y?(b#K{;n+n&w77h!LI zOeRSvI0Xwo@No<~%q$RCjB-W@b3s>cugYj-q7UlOFp~mbVpMp0wV72sz!vv$SPFdl z=QOsx!OQj5F&$SI6-({kh6Vc9%-J)%>#YVP%nv1?J3RKWPw7Ee{r+K3;*LwI7`|^!Fv}?h#ASd8LdAbLjuJ86rmQOzov{@~HI!4!J_TJ}wiP_C7 zwbGlL!+r6D)v!UQIH27WKvA;C%sZzd`C_Hx#j{a0sj;#X=}PEgUh60=xS2Hi#SrGf zYn?M47wU~Z=y>0*fp-@Qjt~yJ(ee#EN=ki6axXN4j$`=h?O+sd-K?BjA7iI3TB052 zrs9OVR77ruNm8Q~Ll7*~ECMHA)w&F7YFs>q|7gMwXs!Al$S=x<_p!i^$&h*H2b^tYQ4%rC&h=@M{uCHxT2O!(JiC8Ct? z14OBNo(WorHzLvowoh^;;Ej^8i>HZYtJ(f+H|4|*O*vQl)mr0U-3_T(qx$t4dynUt zUCw8w;GWP0-Knz2|Cqc06i9C>U5j{ay?_u_oNo(TgF+o$qhcue~e(3_q0 zbo}6VTxG*c?K{{7JEqf@8~-j%s#kvkF=wY;4=ch&4a_@ZwoV*ZTlKos?{iuLk{>IF ziNB1+E#JuJ7)RKPdomNHD-j@!BV-VeNe&E9hl$Sp`LUUYF}j{pqJ*}IP?IBdnPzis zHA^c#!iSwCh{-I4XT@&!mC|*z%yR|Dr^IfVQ1{#F7XyuLeYC*k`}-FdN}!-G@2C1&{aR0bIy#({| zmBdqX!IziioMbKji04?tk%>62Orgbi{Rw~YALqIuUT=g8Z)+Nj~ILbTMgUsC20xr{;K~NaHn&3&UDc1B`2$c`CAoBAo$`lqdN+V;(>Imy=s7u#o;AjY#?Uk*6 z;G1Bd%T&Dv!AMj%qe!v&wh_T`YJW6Xn3ABergb10WEm%h5*JS|>t z#&GNnrs|nle(770nvC$WCJPnJc0Mha+3lYIli%E7w?5WKo>S~Q;;f<&WiigbP27$n z7nZFfg`OlE{@6(v5pUiOMp&ow&q}e1#4bjt+w2UM6ouo~EMa!XynHF{=pS~LplR(H zkGr)zPS`pAol8e}Wk#V(r@&(Su`(9isWKo&CEh#Tt|n3A(lE^lGry10p&aE~V3=O- zx_KaOy{ujnCT$N~gqp5gy{f?)p!<@*AcL&sMZDY%d4794->n1d=#+50OTHM~kaMb; z3aypO1Hz zAw?EpuQWek$bQ(e3+O=A31DnSRB>#gv73Uw>6SO3%Cm~Ir}5~a8Gehc}6#d zVHv5BoGWKa!fS5HglGe#&o^7O%~W2Haeh)CefZ2ZVT9Mutf7#GO-m9qbb}rhyZ!id zx7vC3)x0Ms*23=6c`~o7{?e~1YtnLD)Q2d zSRh1qyRF1B{9R`z|MfQ0pKIW4)tzV-qXUIpb7;5AM#!z4%0&zTdk)yd>!5|X9Gd4V zcWxHp+Iu+xq~nC^Cl*EHweW^Xc?FeBeXSeG>Fn1j{4?;XPm=APGsNX5U?$5^#yp8z zf-}E4BvmO5{ji==_K5L9az%W8`LeGA-&(-qpvUme;s#>EVULYt^K-JfF z@`LY1nT^K-^Ee|$5IOe2Uyh5?=~(LVZ;kor116mFyl)m zDQ^h=y|^S%`REE7>PV*NfumWCUodIi_&{vevN9~xt{RtBnI~j9fdxF231N><`D65^ zAm^=D%S)%v_w}BxLU6$^`|`_H7`K{W>SQo-2B}Qf{V{=6rcOxwV?sYA`-$Km;Vk=1 zSqfa0>78W?xy{wUsUHa)QA-T5o!NPC4(*1IuTMQaFCDI>MPUI8T_hEIGdzU8@-oDj z`Ij-d)n9QaIk4KvPhaM5Y#f&>1_v16iI{01%`>hcsQ38xdN+>M(jOSftue3X2wS=W z^~72)Q(dgea4p`v#PPZ^|XR(EqSpr$FCYZQlAB#F{X$1~WB9 z1Qdy{%9;r1pb1f5VY=&;T{-A5GxQ*C14lWreG*ReS*Qvk&V2@Z=`VgidYWrEfRjh? zQ{ZE2iS=^UDqVeOneQf)SVo-bbI6M?KD1*!@MEKX-=vc5bUBe)Y=xw*@B~tAX!^+6g13cRO;vH-!sM3(~Lqn+Jt69xT!;l(Kp4 zU@cxS2MvfIU&iMCw`)SA+q3j6rkkB$t0RU7k$Jbk?Hl27^Hbqk0~KfAyVJh4AB2d& z?S(7X8=-bbo>h)`?WZUAWKd&r!jix#)i6(Hh<4bfdBG>tS&#;vM{FVI%GZrFWO*t^ zRCW0*yp#|-D@DoS3yk_=EVLc`VC5!Y1i& zza3<-e(U)Z9;c~i5FnyX#;TNUT9uZ*%H`5gUX`&p*G-+14PPwLNFxw%T4DtdOXuQg zG+(q*Wd!wcLUqI~RY>4X6Qb|?|elUBKOY(;mh417?c=kK)N z(?@=x*G3(4O1#QioJP6E9p~UZr>XUN+%h>%X?ep!(bzOq6t6Qk0lGQLXdMi|78pnn z6JN;;{p`kUQ2ztBm=Jd*6Nj3LbqUCmvMnqXpyTCszQ(gy-mlAz_y1+}y*S;tJDk*li5E~WVOyRn?YhpEzyo@7DdcHaul#S zovF{6v*eQ52}BN5zh^M@N8jA+Nx-PXf884)fQ0MmUsbT!)fx{hw4evS1^PTI(4bMDZD5xS}(w8h& zfvw!k1vnjtYcN)YP>t^=xd!=WcH^$^nie`-3uo!avb#hPm#${FX{)h#{m=he^t z9kjQw8xPBV;t%#$RC_gUwM}Xk+UpqtfF1dDjUk=SsiO~ z+sU}4uGEYnbLE-6Z(nY8w~=JJ-&>eZp@&s^Pw4Nu?DvYwIZF7i=LCG|8bzpnuKnQq zF}tB-|A!so(C_6a;P(GJ0`^PrPt*qLng0xQqM49|2{$A}IkMFZ`R$u_oI5npC`?Kf ziBTM`9DZ!QjHeKe)*eVd#1GH(uWuRZC=5mG(dtywzjbjl*Aj8~GFu)mco8{R&Wor{ zvPPm~#!xH9ESV}!oUoee^}G=wiH~!?r|jjcs!k9Y=XTB5*Haaz|2h z8vH;g82k)R_1&h52jyRqxJ5$Mm$mP$4T()n4L(JD7R+hi`91Ocn%C3^mQ}$?mMoG3 z|CAAEcL1l-k$3|)LknbIe88BRt6bo$^!>s8;1Bz&-FDWzb6gGcqPA5`H+MB^nrG=teFj3B$M3 zoPJ~W`nYt!qmA>#F-iY$vVu(9hGaB6&N^K_i{3smokk=eIKWpOAMU-kA8D3|a{P72 z#RM!~zc$yz5y|AWaDr|kE?BD5?#%+LLL_~-^bKrz=CU@>@^>MbLAR_)QnfFRr%E2z z5on?I&F=$lufS%e0fz*quN}N-a|4Ibw}=XVS-7PeiP&$xM%sLUDr!4c=JsHHK?z{> zb}g&*Lxd=}O^;=Ja{sKEC{~TfmF#J|AJ(bZS+I9bYfm2P`d`E77diJ4O#Tl0Mkz%b&6<_^8subY7C8OQqwwhj zrB46%nbH(-d6aX{&EL-J>xax5qT;KN-=eQ~Us%Bw34fcC@oA1blRcIQvO;@kB!I7B zMYX3n>Goh!ZfE#o`f^FoZg-S6p$CW^KxI2ZFQ_m@4m zu^T#hkA&1hvDDhtLVx~J{_;zxy(H(B(mr;;;c zQ*GMgBe6+uTip(Sp?0;l+3z*gqBhq{i)bK(=5f5s9H2`9&olsGhws&H zisAIgP5U{yd^z0>r#ELNofpd$x&uDjadmzuD74sWy2SA|;#=W-hsLcmNJ+UMQ%0Ma zYL{rqmIh~&_Q6zpPoimHzw%Q<p5IEA4y>{r1gTP5AP5lUSfoe0Mxq#E9id_`Pe;qxC*_{AZa*Q*UdP zxdvaVGOPYAU^H@iuFv*cbfmjd4g#!tbZbo67<0bSS1L8L(qx+DdIq>90hD);o<6ebl6)KXVYSs)s+N2sY!+4o;BBfRH*-EEG# z1WX{GGqA%MVXA4PcXA<7m)pSQvbRXgVjznCL1k50@U|u>^w`y+{e1?mpg*-QHFcjJ*PJyC8{aOeP3_AM1Ue zcW8R!gx-5@To)A4NGv=pf(Xiep@>iB3%+xG@(Q*#j`Y1`#%VfrL?1l|H63RtI%Zqv zFL8X9CQF}TDyp>JpLc!t86&^d?6V*IkiN)i`<13?RPrWOcK~7}cNgx8e$5q>1JPEZ ziu3~3#sn6pZzd|3_O*o!-N*?I6l|XMBz_$Jszhs+Ndf2>DeBOIqXEUPl|+BINmNyh zuSXhVTR-5(syd*T@Z79%+aF2P!Pr~`_r0C^am2ND-=`S4=rp6Zc0JGZ4DOWn&-%na zM9t3Xs_qedt6&1;6%OQ@4g1ig+MSh3RT3^52mRHq`^DCJ<*38W#{%tBl8fi&{H$fU zd7&pCtVGT!%USaiGFuJ(1+(_e~Dc`bwvH8BokEP*u-60pY0F1EoiLBuY*O{l4Ve>SB*yc$TW zA~j8yuH&nzi^1dl>PTB#kGulvlZBRMKnyCrfOmMNDWtB7=`P9c*ZM%I%xSPMp0 zEz|H6oX(n@@)VmrwJqux@2%c^Z8A}1`dUq3$ic1;-|rm_om^L!{!YZMaI3eY^noM# zu&a2UiL%yIULIXJFF)1%6buW95b_y!jit)TNzPJ)FcPMx$H$nfyOedy0%7(e|IV zMtm~l!)AMjKTw)Zc+Jf);ArCk0^A)t){T&Q8IT_aX6O?n}UIaRe zl-B0gujkeuozH-tsV|jH)Ws;z8J)%=iVSfJr=!=7irn*GS<`Vo#$CLniEU^AqxQ_# zAav|!qV=i1aGu00=DtMEXr8rQ_5+*QejT(zc@rl=0siniv}uGQNP^wyB#J=go4%0| z9i)ZS4!!l^s!J(m{S?luo7D^wKFfbs=q~m<6vT}(_2s))Klt1S$8(<;NZr53ymW+?HN1O); zXYORe_6+?!U)=?Yf#L<8^@jewk)o#%AhZM)G!yFY=K@Q_ zgIzsu+*#LlnzbKr+OvE8ix*F+=N2v(aVCbIKc|>#*{6)i^rztQ%DOrrIaH%Q=q<%C2O^k z6{m{Lg`|mgsh0S<*GpW=sb`t-ql77s^KK7Ztm^AEU*CSkMenh%c5VKrenf^5wK6e+ zq^23Aw27G@c~d}}N5jV`Qf}f1fcKuLjfmjXTcT@;l*7R6J8<*hWqlNvBIHHvngf{1(S8*!C%Ha>kffCaMMEM^7UuaT#iH z;RBWVPo1i%_tzKtZWu_LV83Hxc0^eHIj8>)%Yvv2ixBYt_AK=;LT zg7Cv5(0jDXIJ{c>E0RrRBI~xN;5YE=mS0Ag&NXkU886ZmT}>G*t58o#~TrrwN9(7dkWsgKl3&dV4i9XGI0(}ilI8CU>{xNnZO0ys(&2E7o z+R)bg?K48$xLNLY)$Oh)spoox)zDuLY!~KaOV~?EF%8Ed zAe84ug9HqNUQrUW{-2w=x78x`!|O?4)6IPrUd#263A1&rQE$Z6d ze|sa~+|hD`l-4pZOt{!84FhYBaJ4@!>}Dy8H8b|m(ad<*(bgU%=-_W~Q)B!`DNCW} zjh=;b<&>MW1nccHbliuxEJYkQ@VC1So&}We;wS7nnYg5*xyVQ-o2RL%tT}RN(`=)N zK;T`yy-x?0Jyr*WJy+(eu0#wkEs%(fJG{!C-vynB-W>~lX}dxnirqGbLCt$|Q{-Mq zl)ImrjrDl51iw$`QZ9drRPvYUH(q*w`__MZAy?X*i1LdQUyBeWY1n zcMqXXw9K^YKvQb|d=sD3;-J(aKhX5pFp|vn^%{K!>R9$yp-1zZ8@1D5S?-Nl9ycq1 zd@g&{lN`vXB-?a50{Tcm zc3Qt0HNN-j`zTHA!u-yKg#P4O#7GoPX{h$w$(G}v{m%~NgF+?NRp-{|R6V`P3_(Vp zN!9cNMj9@W=e}*eXfCITQyWtp=_P1xx+GR2hOg{UTJH>uT<_KaK&jbdCHu4_aXs0! zs2liw%gk{$y*?aWCPAwg5vn%5Y<#wdr_- zlKtJ7(kTLUM>?vAF`yXv2kXRD3YH~xu+_gZgR3$==r1qotQJ{h~{&oMh3_^=cbL zM-HnpG@;ewZx^+1^-G>8pB+CB#Y;C?(l&^U)o0?`uM6|!VjaS~J6rM14WmT{sWyrHqmI||&8eY&n9rwN;3@gO>f{C)PM9XKv(ZE7w}zub zgKh_yZ0xSyGOoFo)N|N_4=k$JGA6c%A)D2pUYjuBGOp6NRS~W^2O3U!TeZB-QfQW} zkSsp6c#i&hntDg^nj#HxSIFz=kuy$7oC|`t<_~@x|9*;QS`zS(NM^=m5yoN~s3r&U zmucAJ_BP$IJi2qO`9JKvbySpX_b;x3lF|}NcY~nPAt4RY-6h>!f~1r*NQ?o}-Hk(o zFiJO4A~AH%Ff->ypZ7h__wC>L{d3M*?^?5F&D?9|zOUN*+Mm5YdtXZB29H0MeHIr! z0V>p%XGGnRWtRJrBd%TBxQ{daeNHBat0;yEw{hM+GeRwe8kViZ6RRwF7)z#-S>;5J z00ngprhZhA&^(b0~pL?J*1@db)$n$jNj(}tbCk59rv#MVxkezX8OMg#nF;FvodC_tn8 zY#thhyj~-sI)8YdruaS@?Zr&L(v#O-}0{q5p(-@_f>Le3v+dt9?{Pq)rtu{ zN;<-izc5vceq5TPBh4Fn!+AS3G2bF$KyWzEip<9A{tEvgUSESO)RvKN_1myc0bQk8+&$Q^r8VYBA$K!AZngWsi>z=3~9c z4V$pNb=h~i`bMZdE6}mzM(5H8oy;3cF{c|0@Jjd0b$WfS4hw0;1yj$1xN2e3^4u0E zFduw(Uts9%viD$QdFyTPv_?tJ@urya8=oe?*dvHG5ofLWaCh~gPU`sQMDT&Dxi5el z>s3E+t^wNp0>iXC1?du4_))k$(A@9r$6NJp%$fp(p|LabjO#?+8#`GyIAz@=I>q364Z+z)qlSKp;gzH*?QXds(j)jOX!s%$RKjX9mIkGD!*Zx+Pjo+OmdU8#@7?=1LS?wa(O)s=rx0mJd+hJHmU@y|WOsjI-B*#&gmoVAp(0 z2@F?9O7szOfm{GIt)-nz0jflDhMyeCElR+BtxG_P54x}^p)O4d9SCQ-8sd$)B3sAA z`BsG_`lRZoC0=YvbszYWyFyXzba_n9W$7=qiN6Dd*}1PtKJb{-&JNm9d}cmLM=V=I zru}l}mbNnHwyt>JGX*>M&zJtoS616pyuj&Rpz=Y7wsM4O`oIN?NzRWPuJ92%VB|=5 zTVA=hR!hs}%EqZb)OpPP%gU+m1oT6@TG6+`b$+#3XfDWIM*Q-1u&$Fj`c8ADm zy!aEve(sk$7L8GW)J5~Ku4T9K)8spPQk zN(K(K-q>}sdy(_$Rpwdei>*8TJ&(SR$nv@E83c87H{UBj2qnd+RnRZssX@|iOE@J^ zbfS9dZPU`Y$*d_Z4?vBHh1^%#)NiW<73J+g0&qZ&`9Lp~?w^g6#1aQgPJ_=p4?gmW z8jO$VAwwG3H&PMI=3L<89~3l05FsTp!g1LssLGR3rZ_CSm1Y zGHq!%9E7OPC@@(RK7vS~#$_Z*C?yP=f9xYWnkm}vYHpuZMlw(h64v0GRRh_ zGmfC-1Y3X;7BgV7lEO8P!MjL_blwH$Qi6|?sYE{yyt#PaIt{T9xyIip`hTU%6cl#yQyPG!5 zoRRYzpfAT=zs&~uC|*iCE=|kt5AEyU>6!+OJizIu8C7O{P04h=W8fVvwo8E2Pkd!_m6om#eR!9m$vFnPWA;+>@u5q@#d2i=33ew_UdeiPM39o3q7 z|C(zo^M{lN&{uFAP=&uV=NQa7o&gVe4ubt;joFRUo_jP9Io`*>X$HI|X<|b)k2p6gv~}La zi|9qH#7;S;HFS4=CGWBw3W~=~?rOO9yc`Ma)Q2=Gsiu;iCXB-?=#A!ud54NuAKt!Y zH$z>R!NFNr+_}4;%kgAz1o9;o+uJ=WY>xd3lI=aH*14CO+lrdlM0_xwZgW{^Fy!|C z&d(DMp%yeeS{K$D`_txc)P5_z8i*yJPUcU?b|zgsIg4r;oMZHG)e4;H`N*mp2V0b8 zBJ4N9z16;a%Xrsry4=*(Eb1AHB582eQ>}^^RxlSFn?K%MzSXcliqQYTg`zUalQs-) z51?Aa6N5N<17r}A2wSwILWP&;XB@Xki1xEr2dZ8QP0ITsIBQ?R@&x#F8pCvzp&JP;_!hZp7nA?CLMsr)&ds1rB zX%KtaJO$6+O9|@%s^M2Sll(MUbrT>$D|B}Fz-vhbokP|}479?Cn+nx=;tr0mJ4Z?! z)wG9Gx~Yo5uDO1vwX*uQ!HD?kql+~)-&>%B#s$Xqz3t*xGc@NTQ9d3{C5DD17Ca35 zi2{>}HRnqLl6e8asR?J*au`ar9Nfn&Nn?lCT7zrju}g3oZm{pWC>OM z?QZ|x_=EP1U0Sux=!Cxnoy~eSH=LfrQVAQQB8EqKOi&q$LSUG~r=3HL6!BC%0k5$} z%%=~-d$~vV#+Tpn>ey>lk~VSP`@6S8E|AW0}kCF*?|B$m_v&G?EclEse%`s=!pU*JwVsRYZ0iNuK$P3}=zBrD;!pN^)fqN+`{GZqpB2Y(DF@m-;Nc=>&MF^)o_^y| zX!rM${^)0Q@R1Lohr)8WIK8+~U*)Amp~0l3rU=;19hWP!&izcKdE0|)J3#QrwpQgd zmviZ&zbd-1KB7v93sJtBO^rI9k5EeaPjh z;mZehm9giSZ@^=j`=qHb{=;uNS1ez%KU-R<4^S=G-72@T{QKyUR^cm6qonkzO|^NF zxIb6TtQ$tE%Fb6p%S+_DH51aM^EAtZN=Q3CF&r_b&n9I_U*!{I+$l{VtHN>5G^*&uo#0p3y$bP^27=pbFc ziT7cFZdy0()+OHg>U@hwWlq4pR!~kEZ(vM`+BCut^qMlukonZ<-I@}H1aP)X#7gl5 z*|hMMb~3pL5TlxKSSQT3_;H_`LAB9FXl(TzrkO@T1I@c0MvT$I_ zwe@LNKJVFeSGv|26fBZ-ZheGN%x``XEJy^z6lmrC@O&`z_RCxoEa-m`EU>eG5iHDz zBY6OCz^E9CK5=S+$3}iI`Ev+;$9X4yowY0ai6Lfv#7d*rR$030{&ugewq z(bIKa5B5D$oo}#34-nof{G1ESEDl|(%U(UQIhu2@j2;{)|k zvweAqNFF!X>fNJGSc%>Y(ODuluzO^(&h8D~UuRkVVq ztv-C}xXF1^&2F3zQxuNk#1o36{?Z-CsFHtaE6ACV$^Jck#e)f=@-EweS z85qoTB>I&s{3`qu2o71QPFS|KucaI9WO2QTVN!n0VuSaYY-58SxBs?xkndR*%Q~2u zOs4nMtKYHwQG;hx=^ko=bYl%h5ui|cuEZSHLSIg3M_I(%FURt6gQAB$rBhIJZ@`1W zd{DKbHpl>!pOH4qu^Y8?XjBRqQ!lw9Xf(#RIK?^RU+|*bgmKourBJmLm$92heZ0jd z4D#~@?esZH0rHK!lw07>*r!|C4t^;(wvekYejAzcxZgS}tjTsL;%1C|X^q+%Ho)Rk zPB$(OS~pF7)nUuedQ-%cg7IE-j;=RD> zuC9TIxO7saJ;B?JnMIy&D7U3V#E0Cx;>YOznSvBX|u%(PN%?sF8 zq{}Vof9R&v743|J>QC4DJ8{GnB!I1%nAJFPqR^PKB3j>=mxY0 zI2F5|B*EHBF+F$VCi$!^@|9_^qXb2%_Tofp%Z8$2l*Z|BLtx$3N2`TNhtlz{Wj6-# z^f?d(LlDwPkh>9`Ux#M-jr7Nz&{P|I$XBF!`l6Hif$U^rTClEM_+V&s&0%IGdPSqC zh+33zwboO=W~H8kmngpM%Yb%B@<;3+hha_8QBF2%`$ZfDu^ZTPH^liUk?&}2!$fpLiIR>0MDxH_O2gaDKglf$9nda(l7Q<1 zSJEq*?>gpFh!%Kg;{Jo89}JzFf9oVfT!#4WM?pBq8>63{yKqop1!^^?P=yP-Gn2V;5KKOl$+Z_O#; zty$>3gStBu7G23QeZt$t@9d_gq0cZ)xBg6S4wUMpk?FX0D5w2$)-+!5vqtGe_#uQS_>Q+sbTEV?Q!IbG%6)D)}1IMh7f##*`)58 zgf>yGIWMB>#iDI~fxs92iRIBg;AgS^Dvfq)_L)fW@^?oUJFf{Z zJ!5aTEuz(nwQ8=bw}I~@UQD)>+uoju&U0O=aCu=!&`9GhpDt7l74eqe95voZQ7ioU z(;aN#`{Dd$+~Qn8E8rQC$?d;$cY7EIBDTXhmZ(cJ_HCJ7v*AGA_GlR-yBx%H8=K^- z+#gJ{xx&*9(*pubf^M{QRdX>i?lRK|6p-|g@4Xl~Gr4!zA31O@L+-p2@ViWh@ewnp z7tQ4O%=T@glDYbGiu$VZ5`&!tDRQw``a_vP; zo@2Rm(8*C#5I&|g!NfS+eRxY8v(pTETGdUTehla{bH7Ky0C+mo>o`3V8QW->SMjcs zC$M-r|GC5kfQWv#hN=eby?q0gH)gY5^NMN*50w7qEulDv!h2n(A1c=%FfW?wbi+OD z@Uw^_fijj#CE60>0E)$;ByhIq%>&ZRUzo29vb4tBzOokBz9e)X6^eJu&WjaDkfa5W zRR$1)`K8Z9^zPCee5ZvvbspzyWL(*0uIbsm4_;fuTk}x9Q@gzLrQX~yfSz|WO2xq6 zXTH~qd$aZBg!?HYD=BpC*JJ>fEGY%kjk@8Q!>* zG~Z%#dWqAS%X1rt^u!r$71g+u_cNk;xu0O5!ez^!CH67$knf&lKPh+*$6T<+3t$Uw zAn=*DDCewOzn3;^)V-)n8}N+damdw&1oVe(ywH2ouoxNtV1v0nd3eFk)ZQfkj3#OX z_YhGU77X&FN=j#bR_ z?ut-Q5LKB>qV7~*?b4ns)_YX4cx4a+f4bv@I%OGYx7#s(XS+-v#GqV=GUQ7V-)j0> z_iziRg=IyR*L<0RK<&UVJ$KK>Iro{)AJfLN?<6Xgxw)c7`M%k^J(ig&7B;jBbcxkm z4=eeWKCsCNH+H6?*kV}I$K_FHtrVTADAD4~I(3e2D_^LR2CA0#6GN7lkF+a;E9xVGEs_ZSZ4y_9Hw562snu8DUf0PRL$*gt-wFUT0G1~H2MCE#7c2@>D zrgIuL8W)}wt?F;~tcIqV*WaHiT>%f_B7bSs7|&H9Lvp;~i7IqFrTK>JHna82g-EHt|h7ZK|37sm7wQNP`w7>(#)d}DVj%W-;Gw|wlGt}3hP61*dG3vPaO zeZvD&rW@sB=NvjO5IVhU3G;Zg}XwVH|6}KjH_tG9g8~TCTs%-p6DQ+2OGC8 zu@`EqPiL@l&o??{m`+t@WbxY$Un4*_OOuou>qY8Y7p}oTWQ%$@mdBpnHqS58QPa(N zu|>P;k)V;V_z)QTVObGH)F4*;1Az$|8@j56cXQiUl^w66W9Ay~>5=+)0icCNHBe1> z@qTqOzPWU(_q38w??JLs4d>x?l679M&3dWIjzfX_2C-KM-JIR*s(mhyaDo7N?gnOO zctt1E{bbW@scRT$cUyA-s=e%6AF+Uj+^mOwDo|qP0NLRZ)IG+AX&Xe0oJNT^5V(7XQLyZ~i7TGeWh{4_WUP#v^T3;a(L;*S&2YdJWOT>JObmvQYIh5- zl;?`r53k(DxkrF0LQ9hQ1j--Ex;Z{bXpU1cQX!r{w@iPx>6p7|CcEs_8gCLwIPO59 z+jm}XWgQVdB)4I!scde0&FqH8(a+?XvrSNediR|Y==9h(QN9axiYHeJJc^jA_j9z> z@U+9sl)3qIv38GC))iYdD9?py5o$YG9K~z~^g_$k}GGEh9{69{Ssh*I@*Ykm+|r z+NyK9*|C#$8~5`Lo-ur1V`ucVyY^{4q}Wk-j8|;hS+VDnC=Poe7V`qgYCmcqs*#ex zje7W{t(pIH#vP#l4g$HL_91bt{fPm_`?lw6JR;!&YiBjvAl%t_27;vs3~&M97cRJ4 zXFslU43?2C3x8hb`*0R0l(2_?r*!ijA+IGpEi*cp$uJ{XVYnjTH~+=J)L*#Bh5U-`um zmUk=}7jIPZu82P08V%1ka*+lCZ&R#p3Akg6(L4-RMES(Yzgkr2s+v{8 z1zLjy3>9-tnxH7wvq%s20fhn_#p+wl)=@;hE;C0_fz5#_%9ne(ey~sXhhVPamo^(V z&4{8vSLf3V*Jl&c1EfsRg)@N|;!mj}2TIPeM$NcHu4J_*`!fvJ zZa;C;w$C}~EY4-zj}sw8o-Fj>po{WezTmU_JQbxosY$|ViOH6)*V80`B*EbMhF^|| zPh4}fTsu@ORh)Ubt*6bGe49}NGOp1=B00M1Lm7e^Q^*d@rQGvcW$jaNlP}nn_zr2K zx*QHwl2STH=rQJS81GD6b9(|Mtdf;_I)4aas#~hfO0S!vZ(z8B+?uB1TZyf&@r3H6 zh+zSM|H7`;(^t5(!*W%&{zz5}exca_g=q?#>J7GvFR2QLue{Qt==Oh1S(ae>ygt8+ z$Um(nqCFqV_v#G-yxsp39$^ieQsaTXNSfkzyip;a!T$qte@BKg(L&orXEs z@YL?Tcrq<5DhxXnupYAI&-b4k&kxARu5G5T9_NJg_&$7umH$fe)BI_RslY&ju}DD` z-&b|1pibVt!m%WuzNgps#UQ?URf*6o*q;AzviEDXzecM5JmQnhQ5e2KfTRCc>?uuX;V5CI&m@`fwY)r zz|0zBe6hk$8=0?C)WjI@@7{OBE3i%6vws{B9S!k?!Q-Oth`%cz%Q}v6M5K`JI$T#I z;K~W3K_8i}Z+|$CyifmA*TkQ$q_Sr1WFNlY8$#W0jxgoh$V@pU_xe)yiO^f^AmFlK zv;Kk_-Y~OYj%8T@oQi#r;C|TiMg68U-qkJgPGF%E1rUWp_m}rwEk(BGH}_awbXROA zWIJ8~ewST6sVlz~*$jUj`5=8<%Dvy4!w)0)?UUH+v8k3D{=;J2{Ra((EfygM zwh!rOy$iwLJvWY1a5NVTV?0((-@Rbr4C#XeZ_eq0T@T8P8=p=bAOGPTR(bNVp*Vr( zn#0|EnLeuS2E*Sy8cyZCT9Jz!IAF<3Q1n0H;2F}lCt@-xIgb&n(&+CcGo^;@CB_66 zr1k>Udc_IN1y@x+;jIbJGmU+6Gp@0l+cz9U;@{eWvlMkhn(&uEMO^LKF;(sjI->I=~ROW)5tLt(JS*$*wBg7urBtHd9`O!Q!cam-f{aS=5g*3TZ^cvD$C z6>g5+pRFotFqbY(W51h+(&aA|qmQ{B0)@_+s(j7ZVBZ?FPxH}Hms%I*N8R;_qpye2 z?H)XI%;dd-bRv^!&VX9qv;4qrj?{vcmH|2z%imQ9VGZPMCiUX{-`BE}K{N|jJtsajcU zr%YFIZ7=3`=r1c@2VV<`(|In;y8KN^v6s<)7tTx70&Y}Y9az-b-HC1l=9gQ)3+ULAQGj>i@l#O0PUW+{8(R1!f zb|uF|u_vOBLaOec4|Jo|Yg>o+14V)Z&T-Z;R?|&^hVren)O&qV7-Hn?iV^ovt?-j^FgG!^A!g|Q*0dZD-u>8XbN~#6(^FN> zZyXh|n`3VH%*cIGZjE%l09pN)k2b4rM>Py3TRB+YT=M2VrAtGS+Hn~oKR&V#%*)sX zyr{)2jK0+Nwnr?Q`A)45#43Mf7s@dS!0~ltOo7l}7g33m8v(dcB<=CH+6MwhV^2fw z4#%QVQ?6gmio7TU4lPIzvyW^nRVqL!eWLWJp;iOfH+unQW~sM*yKh6@-vmmF==7{? zjyLw1j)qIyjW#TvDJG8F29xoiK^5cWQ?h}g5 z{VY#rSt(RoTSfspCfXtAK$%4(y1A%g-UV{~c;^XP-`0o-xh>almc-g+;)cBAJuQD)I=A1Jr6@Rovm`CQ$2%9=%c z2n$R#Qj`gf+_be!xGvJ+!g*b5pv*G9*Elbpx!iaDn$<{J6SYliCg#vrCz)GQsb(D* zvw4rXwM(OR`SVf8e(Qd!lf63wGky1E#0t03)nO!6H=h{uP0LAa!HE2!HF7 z_?;AmacjzmCTI*1a&Su!zZ&@pUyP{UpJM3lnrL+U3QZdaQc@M=_aY+I%c% zLKl{53RL9v0m@sbPbEN`Y`7O$7gJwQRp=C0@LffIU)Rl%76zP5Jr%f)0OyKl8e{fK z^s!x5+av1;yh-(k&7&3;+(BM*YJ67+nv?gYFCD;fp!#Rj!2#9cj-SfZm)-mhLR_wE za>4t!CrDdIylikf5EV#?;*VuQ@LUcwkHsYkRC-1slEgY@K8N2B6FmQfyHXa}$Fp?y zs&cBh#$*+%OGQr)^~`wt(?$1o!*z2J$n-*>kV&)E&mN|{2!CQo77Iy=xMEST*5?`0 zqm+%PN9m{tUv%I78&)#>KpIUQ;616x z#H2dVn`a=~RPMAjvku*FS0Faj6YMM*bsNI=pIQgyY?CeL)#~((7%>f}_9A(s#CM-* zQx`4AG$}_n@e|t8pZ8Q+Y+?vJpUBy}$m&dVQsA8y{o7&2P`>+`(d3XY>2lN!>v-8^dWz@)LQ=bB7gAJDv+1 zZ$fyQ*S=JNiIyK|cPLQ>D8FcbY$w@uP-;NG)lZ}Q^NDtqm&UnYiV);Rgw@!-O%L4vZyRTG_Z1Ewgd z;HGV#*AAh#Vf}ie_2P{4%YBLhtm<`ycMtv{!%zv$ynPzd5wm(8HAh_VzG4UXWS2Qh2jF`s_P1;b1gn3DsT*Xu7% z9f#O5QZ`6-@@RRD%VF)(_+nbB&)4D^5=HrRz9L2?w1Z<#aGLz4-HcR2r9yrH8Ofh$ z28-bX+*EO~Ax70wQ8mXtph&0oerxPT zAxLM4ummI*V%p-#ws;Qj?h|>IEx(@2nRckw7Pvd?QI&HSXU;VSGgXtqw8HQ_mQ%k) z>paxgr%bDORou@++7Xw`rR>d6(Ds1k)xsYEouDZR1%W=M_e(KTD_`Y_P8P`Q-f~(T zF@_^9LNo@G<#3Qm&aErXpdKr=ad>Xo$F74Qx{p|%^_Qps1eMaZXE=F_ayRbfUz6~m28`b)-V^$BTh@8VxRHMeeVd5#P^ z53#~*QD>H8uMR2;c&;r8xI7HMrn^;E&+$4xSp*{6k~`sCxB|rdwD!wY8~ADaVl6pC zBog#L5LrcI>c4C2edwsrfZl;pU3~*l7cfK2h zU1yef(2`kZAuF0cDZoPSI?>~AU!jOtfNSZz@$?Tp@=|0bV&x|5$>03$0z%x?({2CQ zeh1$3S~O8ic;+$>NU6$YFzZUi>XL#Nk(o{%0*U47UncW3RHF&sNMfVY9+Yb~z8AP@ z4Wtf*#jty3G2M9TKVsk|7JIngdESuYzs;7Vl3IOD3I)vY33PDy3dZ9%XjeZF^m`9R zi)km>Nfov#e()?aLR667V%LhZOO#Hri+i_Z`>6h|A3)DS(Gsq^BKz!jSFpR$7!t)8-t(Q#DLa^wR#khUafbz+3mNl?}h0UnSX0h>qPQ7FZfd3 zFTGGk{zM>bWFM2#Uw7?IS4he)KCAmFPB*`s4@snL82cLIKbQ-&sOt_`%l^v_kUu=~ zYet2JYP9J+%o6bVMCm>S$Ch`3El9nj`c31Y{%7{R|&B*w0s)>)t_iR__=v*R1>DvBElA}yYdPL3! zq%@hWxpXQ_+CL?9kNu%L$bgTp{75)5@o5)kw`I;*8%d#-tR9^!3%KukW`{!x?x1Zm5Uk@-{0q;V*CM^k|LK**7h5n63{Wo7j z>wtuog}ZG3e!~94LCw#-mP2{AU%psbbKe_SVt*cw|IeRkq3D2LS6cC(%k{qPEIW;h5wJV{NJmXe{w}$vf0y8Ym*8Iv zr~fIc|J$JYo9qA8f`4an{>=#eyRZM%f`4yg{kt*yAItH-OYpx-@K1*Ce`44C*B1P* zE%+Z=(SQ8x|1CH&5z30YT;r6@iExW>yg>L|?(BD>oy+`(=5;6vNG8V<(@J1B&{8dX zZi0P}^?P6nvBc$s5GUFno`ApNx|cYipKJxZvC_|Yhjl6@+^9_(9Ou?-!n1xE_m;nn zd#V>;*JyAd1fCprAgM6+p8+{M>RSZS)1~T*`jo2X7oRl+d<*}l-q!^12!w46^^0%9 zq4w8D9SGrcB$+;OPIWI(9Asn3qxEy JW#PtgxD8{K<qWct)#&n@5a=G4m>=6@S% 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∈9-l<2{X(fP!L+HWLyw^csLTSU~BN02i-#=1LQ32uCRYp%3J)kkONor)rn zKPFiMc5irge|#=YI|hG-;qoB0N7y04eVYEXN|0m!gQyr|k&xEeDtRiH$Fb+Rp33^P10#F0ED^=+Yqr2p zM9H2yH^DMobN5sac=dEyp-#w>sQBpd=i1V{3etr-E#Gg3v&sxKy2lk83h(!|LN&iC z0ieTZ?`LHA{v)UCyOOq867}fxI1HSya?b}9E0jAPISZ0Nyv$qb-&NVaugm3%xZC z%ZP2rpon=7V-T}g>0nM?Ksbx&pgjY>{u6zn++tmJ@bH>lnMvV?H)9pHTHQRM58qs9 zu}tbd4Ok$)H}&S{eU|cFr*w(VbCkBVhLuT~HY+#m01ws$D{rko z#nDB@sxrmz*8)M2T$aN){;JtzV7UdD2xc{%>m457_aCf83!3;$%BlLu?8Rr4W0qF?OW%)TiXf`>y-~wN~I--bU?;Ag2Gb&%Vcn}hN{`wnZ zWEs#XlZZB_JOiNRG30(J?h_kwahIG|C_DDr5Uyz$j(eSmDmlT)Sab}`z4PoQ@U#1I zEO#l?1PH)v)gB-kO$qJUH9>f`3U{My30ROwC0!96GIF+eGDZd2c9pTDi#MN zEk_iB&au~2!BRh-YP32K(v2N`wcQ4&@V zJNSt?v1018$yV?)h54&BZV9)I_YE(!;awuTaU{b^9ZH&@a3^o$zomwKsy?J~IzP20 zWN!VcZ4A$%84J_vI5&5L)wUG0RPzi~{i-F8ZX1ov!J;qM(aN?MJ~r-`S@d-7SPAPY zNsslDw`D;kp;#!@r;rm)|l8v@c(*7K|9dKRJhqY2GoNsv3 zYH{z*)u=rl#@@R9#{;0%HSmWIXQquLD*KNw+CFkjJq!u_Mx;2x4$6biB}o_rOfgU~ zJUFKKV*^$t$DjIp8}K}^0beWrWMQ7P{us>l3q=muKUo;|9v}F0fLC7UMNtQ4yt0c8 znpBVo%tZgb?6tG{{wB%g)lI$1*74^%FK<1#?ps^6C#~j>nd3K%!Xn9<0inOc zh)v@JT7`cl0K;vXzJ&H!nwy-|lz~t#7;U-*apKYt@M$AgHJ$-D$Navz!44XQ}y|!{d4!1p|Ne^3X)EzDAJ> zIkL+}V8qAp?TyFNZ6Y1lmJkWoG%^)RjVZpWqPf#9wJkAI;x9mwM9kiP4!PJEFa7gM z0n%vz%=rTkZ&11N)+st0VQBM;rd26LzrRBo-$e@?aDX{kU~6B^e*cvYIv|g9ii{C_ zc}n;^;U&Ymmvo0Y@|H))$|JSZ4K@!*zS7jF7mceJxa^@@mh2kdaPQcefWYkVHpRc0 zG~@wv%c5tWa##@Q6dwb6pxh~6fCdCA1i$wENDJLD)ou7(U{}YKQ+vzeCQ>b{J@`yffViX5hCNCp}mvyc#!Y zY)-y~JR}9RNk8_FBi<@4Pej^t{)pn+8}F-Ok94)}655JAYnDY^O~Hx@R}+-eDlp?N z($~7MhdM&E&Sk^8Zu2iZ1P9p_z zD2|i`2f?YK9MuE1DY;y80Yhg~+%DKzGk zHmFS6(Bko^0R13qKhx}l-XV-Vae)%={i+E0^V9;JE6Rt!rX`wlR*hTIXsI{vklqGm zNI|u}4ZAy-)Oc&_ixlz$my^VrWqef**tEIsRe3XNG37!!x4#UUG!&TkRNAanWy9Up zvi;*x=jrc3S7hcM>@Aa=IwkdLCi&y4KXhFO_;#EZ9@n67@*_BVgDC5&enW$APsqmT(v1=JmBH$xO@kv72yXM~r5 zu`(X)Cz(Xz!2#fDe^n05LAoaSrH`;zq&f86M-r}uIt##<731be~ zd#m59?Hx|z1sfnGG%iMQWRKjqL_r_4hp4odF7mVN3J0Z&tDj~raZ{4MF~k{Eo|LyZ4*qA~~wxs{;+>ZfmOm{S<(BWi#tOw z1BZgamgznBOXAtF_44&@(mjdhzViofeJUM+1Z7+I9|iCX4~FeQav97|FSAy<1qx}{ z^_j+_DyM-^if1W54+MHUGgQQMZ)#^w#xE(vov-`9)JNAa5%YA@Ddhm}6F*x|ZeJBL zO|4jXrhs`ws^mWU&Ud+5WIs~bi3dEFPV~?KqFwf#`mw%RSNZ+wfX4e7!11GiPC1j1 zWWv4!3txab`}^Fe>fMQwLx#7@`ED5P`MFNdiB>h8Ce)}}YCshqOnIKTRXW={^V+kxH8MjbbXt|y zcVkW%15#q&8(EEYTi&I6$0(l5@!qguW7yi2A$T~S{O(cFqq`ZTYIo-QmtZ>H0EWnW z@6B;UXIb+84U-cRhx`Xp<7*ATqiEHBu=--`1&oI4ZI$L zEmZhAEBbBgoJUa;wt^7ZXm^F=)(dpA(t9Q8kJvT<()8uVWri!;o0oBHxp>4&03!|XdVF|4k-QVN&+GCCBRF<{HlNf5 z>i|e_ZYzNW0ABLdCWdBOlP}00ohY(2`j7tct3>X7=u+Ts(B*zU@edlAs{QiuZ5KKK zWty0wU)h(V9GKlacMcfDO_hKevHKA1E6m`LY~&NzPI z@P$FoU#q`A5Fh^D6+F4NzXXZ@bO|`GiKtVT6o!7pJ#Be=W$GbA^xTgu>C3(U86I{V z3Iv-TjT66+9lzS*Yp{g}GHEdYAgp}^_SYt22vE}^!z2$Ga)Xkx)x`H7ue4M_7l|Vk zLD{NQt&f(M{_U6g#RZB0TUaG-`dcb6a3axgT$qoNaR=4FJx!xha$8$D;}ZdTEY->2 z7k_+@4-gha?f77@w!LYI5(LSPtu)B=(30ue;;1|IeAfMUSNGxVdvt5W>clU%T?xuKdvs{LyoN*~(wL@<%)H zN6-BquoZx6Tr?KLwEX*?P^CxVZy zx12x4z{iICN6+#feZ(I>clOABCI&Da`?3=NHBS^Y`6e0szv;z1dp!9oS~InPW%(s= z)%;-|Z6yFJ)yw=(G$8+y*Sz}9xK9Jb{xd=P&mfH7Cq&0R_P-g>FuVWF_H)etP&4#9 z2gF--m@CEVCqgD7v z$Pn}54_&&DJiFc!KZ^#}x8dvxq&MTK9V%J3e{@Oy8;Ejpzi=;w00jB3V)Ng&_dns% zp@fl3d^gdL({6Yzo0Tl~J~+}8n8U2#d6k7jVfj-Ye54K%>9{s32uMt0f=@4~mOXoN zAqEPXEx!!RFR=Hv5Wzs=$EqksxZ6l7Bu3uDulI0WHfgb(qUW<>0cYcKWt= zCG-6$d3fhJFjYY|_Mf8W-*4u#zOtY~^Gd8|E+8ST-e`_s@60K>+#JdjPei2xN_xI^ z{EM@v7*HhN?YFvOZUaWtLzUZwiMQ-_`E^iTaDQOI7J#Y^9t@=}EQPGy#SP+vHF17i zqFE2cFxaXR8>036#<&)AA;*m;KKk^!@09gT+dnhZ1FyLD+aQ90CY(RmScJ~wxzFds ze7kN#k8Tp6SCmkfriqTg@4rYqF|vZDe=}5UnsY&^d9J3dT72>+=m| zP8o{L2B78=SrZyo3_UCeP+&;J;AaGdt%lzz*Lhef+g~=Ji)hiP(AMe8CnC$QZ^$-i zJ6>~-&(2ITFwy*A|DVsiIu6@MPM-d=JK{IV)CxWDBcSg2iO2vLmPn29-46@1<%IT> za>7PkqZkVf*5fd9<(Jh2pvYBzl-4IurJO@Ia)`6iOM$h8dDIQ|lF6@(&>_pz>BOJL z7a*gIH_iWi7eCSiyU6#?UWnhCCqjoSyI}=W>C!2JqVetRmKtsm6|Wl>TST9l?^myO zv(C1QeRy8N{!swJxjm!Ps-KT@w9*Cj;kJ*SUarQCWO3UVlZ?Trs}JZgTdCA0?R}Be zK4&)oH&sqo3=jr%3-RNStu~1w)Hh1=%qNCU$>l4j}e|1i|$ky3H#eEg;XatoUXhqv{v)CA)IA#emFI zCBQ||fA>CV|Gvpv4rrr&2?sH!*kx!jqlhgF$sC{q;{=U98xhr#ZobN= zrw%=+rhc(1V7u+Dqi9=OwaR0mQM)Eth0)62UV5r_}&$Dv5lW)fz{mW_=hW!VA&T>oc8j z>A@&KZ4Qxhc^+WPa!kKxp|0x1s2jDmg_0GwRli;K&_gwuKTY(})*@P1HRg@Q_i93+ zd5K%^1M0Rp+&VU;Sub_gt;B7ztCG6i8m-PV{b)S_ZFm<+(9l| zqCGNd{j~e|-sMy`$EG~cr$D-w*CCngyGb?^?%Oak8Y%NpTdG-!!?25>J+ij}UgAxW z9z{qCa9itMpEB*+ZF(Ogc=wd$^}h)R{3+Cv70_vDmR9VoaMFK*E*^(;{A^Zt#JrE-3uo>4dJr0(OC$TYm-yb)lc;bR=Lt`} zHQY4V!bMPMkPmSc)J9;u7UQ(}LF;AhIS3CpQDf5}UcU3=EBawFaSn8|?ST_^z()vCIxSplPQO~_2MLu%W3rHdM#s8y{ z2L2T2t1{<)>@&0l=p))21xz3YA7xk|it$wqbHtH?J5R{pdUU;xn#kVmQ^TN|Aeh{h zE}_K0s|Y1)xXlVZJ_-+cHr%g+6xQzi*q~%Rw*8KrtF2IQy+3!m7;gEV>REe=Q?#Ss z*%v%{kIs6C)XxiKTlggFPD7QzV9ia>sfVYh=lkogC#}g*8Ggu4r7CQ0+b&b7HDRg}mW^#YuvT z2PHm0<9`ocoq~}Q=^kOAy5Jg}D;}07mL6{_Qw<53Pd#=oInk?g2n{S+`Otm_X_)xl zw<(laCay%5eRSJHSM%uI2X1vRC1tdI#*sSvP$Jt%6j~?mzRCO}>99@~#ukq9m5;j% z?kzdEx^vp z@hnLxB&*LE?Kuiv%awLE>p!G-s}tjEdTBEQISd9fp3NAZfxcrLUr%K33M|=Ifc&1$ z`OQP`cl%LO3h1`_JKyk|8rCB7j>_xFmNL+YAsshp!Vn~1A-tEm9vCOMT0fct^E2G8 z3YFGcGRyIit&rVF4!U zM8j8Iw+i$LgPBBoMyMgeV(L7P#TUPK(*v;Qtbp;`Zy1BaA8&W2AZ3QzbS=#rTltR^ zFJq__Qgf?qn1@Ws1PPq|eDa=627m&hTs(zuuN?kV1RnbGDLVXFd~mNDY-3a z;aKt0yT-Z)rvbe-9TIS(+`!Q0K4lTJvXN7Ka0q2K=7CB4oC-RTd-vXu{ccS2Nk?VF z;RM82h|Om-+5O4s_i%R5=F%-Yy-v*{7-_5;mC|aD1+F0>U^*R511D|rDzaV^U}Pk0 zg6QnOJ)!7ZWk&ZS#7}&zCbe}lSv*%QC{iJcAEX!$jJr;@R7(`TL43wo3L4yT!g$S5 zwEzLRJVNI4=B$=t^j0r~$T$R#br~%goS7|lNbKpG?5t?{d0Ql#vq`=EVB zwHah#pn%78s1OZ4b$fQL`QoNO?n}4w&sGw=!!Zq_6JVR)Ug3naRND<1BhNzEZ$?gcww6jep*8Dnd>wRd2x9$y~VgO-Z+ zs7$XhS4NccNr4Y4pa)6^m-1oGtzNt2m@`w`2`dGdzrk^2#i7 zc2guH&xlhG5f(0Y%xLj{`Sc)q$0XkEQwsUGlYfrTdMQVk$DgHjUordoZKUPjlT2)+ zM#}_X`OPr34_NDxCDfUAR$GZY z)l60r31HxkCv-V6UkY^GjcS5=oCVo73`$W%Y$jW&WLP}?_Exw(=31=kEfEnlB16^UUD@=(JAeVuDbLDqbUbdZ`i@ zGxy^Y;Z<*NJXo9ppuvt4tYA_p;p=4uRy{%*V9PCz*T_%g~2*-r{SG4bR9XjwS3 z&MSPU05bCNu6(Ij4UEk^XTjEH(`G8{Z51o)G?F9Q^_SMlM6x$giW0q4@$g%A+N17>Oc{Ng)!ZLh{)IdG*7f9 z)E_K5=4nxmsJS+ynExyVerJ22{laDjCvHjm0oAcPX{XM9p|coOxqv$M-t!J(C_^34 zJ1Hg#Cq4{MO%7o}fanmqb+A!v=`3e~e&ws3pjS-YscJX{`R=7FJ87%yAFBJl=rgYy zJygNX=_tldUDHZdZey~FxvQe~gg4knjQ#sU2v)0-v}az6t=O`M8QVUJcvF{R0c+NOosXhD-I zj8TdY95z_7p`AoMrPY)V5 zq3R%;QNT~7dJlo#9PJhx0T1^mJPyI5G|8Br8}EA`Z(# z>+tWDuxHEl`Om#0%=|N_M@wx~cHHW(>;XFap@~wNV%=mkl`WP}JJz3iY+?K#M}y*5 zxWucTpf;6T4}N+bRPv^4$EU)hw4MfBz@T;xT5JWTiH+=$QMWP-5i zmD8*{#>+TLC68KMnvY zrzeJVd2CKRK9Lb*+N+AH-b>=PuT4~|0d?9OMGpplF$r2RcJM=OWTDN=mn6b=##slY zoZFx5EF-4uLFh+ag5e$8=zRKFr#z=&%Ale+`eK6I{O%zltoL%2VZ&-pojTkZx_V2| zgQl`aw;b*Y3x(jM>xVOo%|Swy5%Zs%On1RKq>tMyt+R~(KmmVGLi{o^!rOcwA3}=% znSp!L;*2#%eNTL9eSg8jtlX#x70DqVGhinUoOJsVdL3Vrl=5dnVNBFam$lI{gT9>2 zA`|=5cE0rSaQ1t?XSCN{(YB`-h7e%G;4`bI@4%^tPkw}ninuQi+7Ddh%n(|*CAN(D zv1S);Pp+XT!fG~vJZE8i90}!J!t5A$F$l6a7dh`fNb9x!w&F6zP*h1h862{KPOFuY z(`xIJ22iO>3Qyy(;x+@%3vxi8yufET6|VBC#rGrQGfN(YNqk-8s5Jy3fEKmK2Ju)R zR{>CrOrl}VLY_^$CjnxP+r}xD+U#97M{a&M9r7{8xd2l4L9HQ(B6NXgXM2i%kA0NX zta=Bj6aCXh!h2JEe6rK?F6_**TjSnktsYtXjY=`cZ51fP=J3IX4y5M(ENtmR>y4+) z*jOJ_*XpgVA?EMBGgGaRyr7i#cm-1_m#BdP{bVQ02h@PwUAnN2=CJy~bY|hCygkrC zO94;7kzCALeCzcz#GUeJF%h55_yo z6}i9+w%O-bjnBNBko>$iR#)lyO|Cg^KmgzQ+#c*=QA*UC*g^ zK3xd^Tty#_6v-87Wwi? zSu&&>=Vowpx5Uup+5rCtktKZ-0PKF|Y)kiNCcR>_Pp&O<{8KLW8&ON`y;q6nPLV`* zA-mfrZkW`@`Ip1iR+KsYCssS8oY9WKf>Om`p-W=h1|W~~wA1s_fQc2YugwUQxW-0u z#MOBa5`ZxQW(@C_aRJ{?eW?kNJ52xaa!5e3dvCfp;*y5N*6h{xG}%N~lf+>l>Z^S-!5$uIaLB23AT1>9fvMU{aTh1=qY# z-Pvh1CfAT#eZd2(S0AjM_Dwll7JuQIb||c^I3b!lSPhVZZ+JN5-~RZxsyxN#dEg{2 zB9$vJ;}(Uyv>_np8%QPscmGUndS;_QU=xwdl)sEh;YDD#c#VjvooPG!+;+ zwvltevUc_I_ud@00K_v@%lvK90~U#|CgY}5i32lF&0mvcr#j-{G2&kS>t_$9Q2+~u z+utSh8$xlt54|xTq7((nm7QXgW}W9v<>`>&UKJO>- z1eknccBddtT9&go9>05!ErLApTq zmw*jH-u4SEH!&@t5Q#0JCWW~CXlNj2$X$CF7C!3%mn@E0o$XogThEgxPrj#L;m-~* zLV{xxIpa4eP=NyYK^nUfE;-=8GH<`JRFR7SU%Qz5&w)3;nKJcN;NtPl$J+q#HdC77 z@%lx1$eG3H?N#VuPoPMUR(-};PA)+#hNWDo45BLqXb--RI5no%by?uP9 zBT)bzN#FUnN@mgv*B!Gg?~AcXQZSbRkd_c>5zK)AnGZ*ribz(C+h6qROc*1TW!>?G z7X{6_#yrW>=iV_1rHpR5@B&7-DW5E8EH+y19u^Q19g$Y^7M6lY07{lPEp`ln&_GR_ zoGLYjZ!A;jUViPNioKpBaVW(91XKJE-ZO85(X1bUqR%|@$p&Q$_jy!HPtR&KPmf=8 z?XLKe#cznRy+ndkl6WLkpC^@FBmw9^ju1a2u1~sp2EuoKi&z!q@KWeDR0(9Fm75NRs<|3l6WNz< zI&4Mum76j^7ehGX5&_OA7jcZ~l*{+e1;(n7?ax>ANMUHQ|MK28^C^b;a#0 zr=r3ln3rw)Pv%@NPB=PY8M@mvO?2hM#V_tx%E<)KRYH}J>ak2{-F z8M1%Al)&H2$mCSnxV}YN&s?v}*=h^NSA{Dn0J!;8l}Ta$NkTWe{)gNm#(qt5)NMMY zp7;h&m!pEO-0%PinSo9lcAhGF>dC>tSel2BDjeap#MEyC?4=e^fWJ-?Sg}sOkH?mpnJRQ*#cg_#M0f=D2WMpOTPV&@f!ab zy?F2D4v-$g-;#Q^;Y2X1uo`~Cc*#GR5?c!%L)4TgOzzRX)N`7Y+Y8r0{EJ*p2(lFO z7cj6Y=y75IWGd@kPdd{c;2i5b37bL|8_0`o8NL1UhZhOsc_@uENhMD4U$e@C5u~y& zCgCYRr>0DeeY?S(Gi&hzdW7{F@^{H1Q#GKfG4m|#fe`SgV`P9COG^hOECBj3sTYX2 zR-+}1V$WBk+{hc3>SS#r7E7M)oVQf@hyc9u&_o5Su?Xf0F&Qd0b&;{{j(K3PH^VHp zbqhOJ%7JPfHqUWNHE7{;4d~5~^H%{dlqb%W#gBHgLl?)Y)rsUMTN*bEE8ce|9vTS# zxZC5sF&CY!mD(=X{LbV~t!0|%1pkR=>80h8GdVr7_~dR3q4w)c7t#_#oxLtuvXDUW=`a7%#u-Op`T!u zU8ZqqyIsp9gI-1gn&=#DHa>0@>P$cK7zuO4VakI7N7Sz;qXl!sSMbd>gjF}Sb$tq>f6?k+@ ze_S{W_Sk%vDz*9(Bn>jhPHBz?Q`g!4*x9= zXAMxLHt?aPY0}QgO#$J3P=)MYv)>jL{W2EFwR$IU_aivu`I^<^M5Tx$c-vYA{)LYa2F?BRa$Cj-!_znK{opu;^pKGz zj+V}5-^t9vr>7t4TUJb^(N%Q^8X1tcSg*(CY4k(nKrbqf_JDZTf==R9KivM(&7Ciz zpHH!UvnOudaUE_bG>p19set%M>(OZi5gE$!b#{rUZblNRC7}qDtv0CLy|Xx4KF;Ar0pB=N>Ns;3oxC2w#R7CR$Dw`uxK2ZPUqDNMh!!CT(7$tAkFChg0p+F=xTBuQ?tmj8hwADhhSxQ%eiz> z!AGA{rr^Qd8*%eO80T(2X9+|nO0=lV0ryM9ugay%ewI+V>EUqQ!ebXb169o!Aj*4E zd$Vq8Ec+|#FQAXZe+T+B%CPw3yNqub zRVDc}^RJB5Ls{EDg3d%Ti=>ZzKh&0|oe^DblC+vpN3o)!L3pD)0D%9{B$tfo=+D#U zwdTjsBkc8e+^lo#`X9zze{!z~M!b@I+cng9+|Y@^`R1;5mwC4`h#O&+6YPjtHuW$F zib8r3t`co}1bfpJV+Y~GDF=-uEI`QRv4TM0e(-w_LJ6dY9)4@8585+C5VRCIV=-vg z*Som3?Gv$Gt1vhBWIbM9lZ|J9^g}mU#wpm?*yJ=I7!H|6r`1b!weOSyw{mOAtHbr2 z1jAV+qDrjQ?7)XqZfK{=MGF`rI(pKORy%8qXsU}KtfY$4htv+BniNrC6mMGkrtEo^j-D@odK6xxpHP%G&1u$CBVTgQh zs0jCwQ#{#IM3GWf!_yC&%m9Y_bi@hYAT5t4y0mIWk#HiybSLqN*{U%rAF5oZqqgb| z!E4(dSRyb9={Sl)T;dRDCLA84omi{|^wdM7&$I|V_99?AVtBE`hnk}|GO8`=D9OqO z2mKha5g&t0(*#d43CA!AVHT7IcT*+pvMC0|8}G1rht~POItwn4Kp|(Act9|({Q(YQ z%J8EI*bHkY0<|mH9JX_GREaFB6!cR){)P*!D}Sf_8TOH0o?6acx>@yth;exb;$CG+ zpajb@P?5m~^Q|>B4FoAlZuqVz6;xB7?djy&VUOzS9jfCyUOPYB$Iqj7#;Td1;$l;= zBCKW6t^E>xz96Uxbij-b1-gSd9t3Yt^b&-*=o7TlA&K^U&#vN>`K7uqcz$ zFMM)?Mo+%jI*CN|!F+(m-l1msv#1jArG0&e$J^TPCe291&8G2Aa$Lf<)hvSLKzTqe zNLLZMnT|CW?2$EW=XYrh7e3aDjd4k26H_8CplQPeN#8;_j5!GkC{)=F@AK3Q1s#g* zulSZv#H(_5bs3?T{!3fj(w?gkyY(}L`72aqpV-ciB7UeN8>rKHRwKKITdsWTFHdGQ z{?pOD7M8!{)@K@4_Da2YpBtVg23+@FtJL4(&5lwz$$LXjHLRmpGl(ay#3f!Ql!@_% zNx5C-z0@}bCxY%<;YiRuDs4TDAT3WCb0^A^K2YjiT%fbmTG9t`+lqC0cbjw{yln5A zrW?E$-%Z|#`W|@v?7^#dj=Vi^j_c6=hKVat@o(nvepWZzc`TD&c#vYx2en;^1qj7_k+(%JpXE00BuGud0Jkr5J% z{BgY(1?RC0io1_HIE`<#Bh=zM@a?0K5dRdpWuK!TUY3ooZ<)p{f3p*hI?LpC%V|jL zMz;oY3X4LE@bjt$;TLC_x2T1sR?`ND6V=KJmCv>w^T=Olgi_Wsa_7^+Er9hRAoo8u>0;V~dY(TN&$F8t_dyb;C#SEv8myh`5ojj~Qrb*^$oY+YmGsgub zX2cK0Y|jj3dGmSAVP%t#2_u$|Pn{Rm*x^~s!_jOmlSZxLflRkGP<<~0um>kw-MXgl zjQIF)obO$+bG>zUbYB52e?~i=7;LE1!JI#Sa<| zR}lc#E6E-N_a?p8%9=hG7qQ*&bVHs>6elsT3tKE7KeQQdE|a?6D?7|)TAi}KexZq2 z3(sIC;!}&gCS%X9nolDaV|F7IY0sR$FeLym2cLy1Fc%ToY-TT%nRs3IYC9)$hCjKW zw79S+LObQQ!o2*;ZxBc?)+VbwyO3RlHoR5U65aO1hfVk`%bC6Fz3C3)cNFHwG6HCJ zxWU7@doL+{bgpk!iii5@qROZTJ$%sR4whuBM~I?IpTzNbDV#4dS;9dp24!|YaRA|L zj<~f)3OLcI0N_r~PRu;dGjhP{^Jzy-sJ0p;JQ7OCQI_9$X*X7-+Xpp@<=k9(7FaPc zn{mM)?A$~?cjPUpaqqN-N^j!V2Aht!)|j8|bcN4wZk^%y_rdK}hSeF%NkTZ^O|8wW z#uBm;L%3KSWo%|={=(S};+#k7sCA-wnODC1-n~;sO`OtKcUS{v)4aL|$7^IAR%!QF zor6!+^nUQQXWtXDwN|iCz4ET%st>}#%hzD0nG?yhvETv66g}R3^RCDALX%=;0^VV; zn1H>}QMD0dJ6?S1yyCd)a%VEhmUDJfK zKbA3Zz#cOyslY+^Ix})9bBVEU+qg`miu^xTXZ<+WgZ4mOa;bWzJ#I3*SW4j&!0(kO z)EzVGtI9C4*sFqedk95Hf@v$r>J==n@Ur>PWltpsM%nieTDXYjaL9!>YyJF7mgGu| zmAu7I5$L*r`%D{XxkRhEt{b|fPsy2afjSklM05EEPM>*-#1;9IO#9u}OxD|_P<-Qu zqH8tBOYn_o>u#+*6Z*shPdctWaXoU7YD00pJ3*2?+qf!BU`Ha#TTtOhd7B?do3;;? zV?IcI2601)GpM=737%*VmWUZqZspV6gF zxr~xyK+wQH$82LMM~TO);%}Iv**r&{WMVZ({9ZRwNvJ&uu>Vf{&Jqcyp<}+iISh76 z^Xc__a-tRIPf8oLB#n0;Jhtd^Msat=$htEDAuNDx58;G9({Xv#I~l@hb4LUBB!vCN z=MTN2T6^<(86Q9-KvkmPFh2oo7O!mm&B%o8AFP~P{32ntVqBY=&~gKV;!w`5oLR$bXZt6 zi7Wfbk{|LsCPfU{LTPDCh@AXeRQ`A7a`qQ<0sXh;^7rWeZ$9T=UbYso@3y&kMdjGE zZe*&Q(th=Z<2O$G@5cHs@A`Ku<|6*GSiQd%``6BLocYTP{_i|syWdv$`%`q$-=Wajb{(t|U zq`B@q^}hXC}%j_$ngZ;Gn_<@UU) z<=$Vcit1mB{cC6cGK2q14;Z@d?WA^BvI9SxYt;>xz4`y@{r;!!^DF-Keqx3_^0#`r zocCN4z+l-7p{Nm%hfz(b>7y@?tj0-cF`V6V95u-Tyo2=<)i417Zt+;HPUgBfz+9*tQ_~W zXb2$B+pdq!W>gc*J+aM;rM*}@3AcBX(|;p#9rI-%0{`;x7O8P|eL93Q;Fh+WC}t7a ztAO9=*Y33K(||{vy&g(lpGgF#5CgUwcj(^U$+B+<4Uw#zLS#!hcultXcQM`V8-q0`?>@BPh3-HKdYJoSY(M(S5zYS25>6 z6{Z-OB4*u@z0GVG8P19m57=AxQd=dB+k#8idt`IADe?;26H4o?83DJ6N@o;C98uIQ ze1ETkxFXO#xh}i%^{{3EyXT5>n1}gApBDZVoAWyXdz6e(n^dR1gxu?7M5A9K@Rrn_ zxv9wUbxzN@%>6iL_hlr;1*jJGB=#%1-Co*KC9&QnFD(YO-spGjtF#*B)Fo<|NWF@W z=H4hx5f7AjYO5k@0C2J0DW~#E8T=^rr749THOV0$x|ickz(~DCRU-2BaA?M z;v6dUYOmkV5Eol2xw5MhQERW0M-tXYFM@nU^vgR|@q0Al-dZwa|5X40OyXMkq0E_z z^WW>p;Pk!X>Q`+Qmq!4ukA*kajAww+j_h zi5S6UcjwU;{HY>qg2oDGxACbi<17nBl~^(4h!3jI^AxDU(O5ro_uQ8lW}nWCw_xFQ z+^omy-F-VF0)_5={CpiVO9JR8oUvFVGZ^Xwdjv~5pIha99jO>{dO51Af7*jX{cHiv)rlpZ%$)EBmQlX>F4jL`JyV7)M zs=n??pmEiTajD~uTVT7$G-@^-lGM@4ZfieqyTR>K{JTKN{;kKhJ_WsX0(7V9a?cBO%zcdD9aUDfqWDQJT5m}@IYY!GH3k@%u^M6)q3 zoNc|OgV`{-5(~^}>)Y-UzjrQWWqDR^C(dVcP{L*>G`?7PA}y|zIJMmo-%ZE-fzW44 zT@%2EOAXCDYK-AkPdO=aTjb^$?2LOaYGV0rCUiho`Dk#U?FVwKPS4=g<0bU4qq*rbgORPn zP8RV~0;w0YTT8d?4QCy@|^1~}vbuMns zCYVA}Zs_M#=hZF^rD_lim{UR?DmaoESmt(X%v`Ih$uI%2tOGNfz|FPEC>pJk&HL*? zEv6opNnEOWg0OgJLeWhT(^2|0Mkc*D#h=HOYwK1T*H0Z+y(^qZ)Bf3Nhl_48bx}GK z&{DhBae2LOAP!VFIRBNOun(tT}=)z>9`ZHN38_l!U$I8B44}mYl&>S^&h0m7O&vf zIz>8FlESYlPS!KU-GPvx66Pm(}kRD z(dCMc1E869bKA;i&aF})y_Gk|tjc9P2oREF?PkqRYlJ<XjLWITD}qe#ARkDgA!? z`dW`+wP>zlSgC2nO)uOahG_1H=!|C=I=6|Mb#^x>|fy<8Oj)`CEK#IKtObVd#B&E?&-K&Y<}QC$m)03R(3-AD8jN`d2dFnee&9{SF%#y zjl0Yu{#Y|NW82xiv7dAi7t_u^6Il7m<2Y!4f{Ly?OiszwynIdG<4OGmE4CachpaQu zKxvE1cAGjxu4U{@*9N>W!+Sf^boW;RjZ@;eGV<2H_~BH2pV@BH$Mv623VV%vF!vYQ zJrB5;L$hk)!cebIg%v%>Z3{NPJ1!DRgI{0kV)iFtrAFj#Kec-7)}N>8>CXR3YF_oo z+pkRFccyZM#&$XJI^?k*{Sbq;-l$K6G*6h%Dp=J1EhBPAlyZ29;Q4N_HXQXcBF@`{oM z>i9^}rG7R+;^avwm&7l`_N&*_?-?3r zdz{&Xe3VKV1xQ)C_Gztyq%&)^F7@-i{Q%`aG|Bi@peU1O*0X8#qQ;vGi}NDNtKw9~ zhyvyo)#SjlKkBEfZRY!CgcqV3NGal%B~?;21^}i|wwMrx&=meA($aAL`9 zLGb)rvhZQ?-i8Z2u>!?aXl~5*r>Bi&V4Q;LbV3$PQMZG?H+J6q^*3?P4V9P|bcIj7 z<@R0=MU;hRSvH(iQIjQ>l$M`EhR2^1Rxe>W^VAAH#Ei^4(!g}zwCz;hkoqD^|J?gv zo0X3T?Q~h(_MWTX-fbO_&#B*oGfa2A=_QU|M7V%b$8d58)uBZ6)Ebb zpL64wman3OjM-k6%qKE!nnT9Jdx#MhmC5V9+JX`89Q7GMQ(UsC3t^sVV3KM2$2o>G zn?Y*zM+&wSWE&U``N|xVUM~rP_QS072=YKZ;tBswsf{K194(EKN?DN|Rz*>CTX9 zP0KJUt$>rhZVe_Hl0P-qM8wR7OO4y4o7awXGM~IYQE}_(ZL+>#LQvCd2Emfxlj^5X z6Im5_UgXL_=g}D3qMW$Cqa&KB=_~|!iP_a!k-1I@ZNa%hy`o?N%kK>JkwDW{a*yYi z;DE)*iriY0va=UC{r&IpGZhcO(u|#t_l5gCW|kg?c6CJnS zeoc4@lo_Utrl&}!;6<*%vqh67OO+Vx{A6jf?^;21u0tiq=<8}q?6nASP~n&2&%zf( z<$#P(68B;<)L}1u*{8y0@26hCEmfIu%EF6L-dxjRfz!vQOkWECIU-N6%Zc)vmA2ap zaD+x!X)o!v_RVjHKgDJEtrdtrHR!(F`cC~+UJqBLmmZrEP=G8KMgLlbTo7er&?ZdY z0Db%}cc()h_SCwvq%x=ctYMuAsC17;x%46-tcbVz97Fh8hnVe_hC|)w4_@I*$tmd` z6;jFlMFdc;=aPxDgVFJ~TrV3o>j-E1ORU{R(vNS=y=0Ygo>qCCbm~ZhHSM+SslsOs zugpG^b7oYBP@}o`U2rBA87Z*Zi42NtL z*tw>u0ZvuLVQ#)pNDKEOY0`&;T!W&N9{TH_gAdiaplDdBn;-YaRhgoH`hlwxMD@3o zuW3ojLpE1mXLAR)7ARW$lurN1UV!`j4vyfGd0ctGh~~F7I%~#$A*R#8!6230b6Z<) zp?b*pegs)KR;*{WQTz2_(fPvC6qYDEWrhETrnicUt82Qp6M_bJ2-Y~JqOWY#)uJoWQ}ad`6H+> z9Kxy}=*?ll+pyWK4qb9YY__)Z#moX(nV{#Nr(RRh_v{wUOH~=P4-d!lLo-^Y@;pTW z_*aq@`Tw~P%0|U_qU23W^(#9F^VC=wTQ&%^+N#rMhwK=EIY-c!)Z~(w2M0M!>g%y~ zQax|E?)U9E=B^rUsE6!?Ux7=KhUO2|>cF3Fsuz}SHWGVqF`NUdo^TkDGu~5s){?fw zaJ<(@N(RS+$(d?%Bt#fh=${8k`p0$af@;GPM%kv=%@zsnf(j|YEcA!Ie=adIh?mR;fqPKQVPrr`o- ziL1)$mCR~5k|Q393_ouzY4xZddz@tK<-{Nu1y--MJUa>bEzUqa$1|vA**|a6JA|A5 z(xg5;OXm1RDLRFCWMt36Kp^JVdrO-EHb zS5!CYQOMMb@((>O`n0=4k3x!rpsm7r7+Xr|IOd954zt-R4MpEs?n;Aj}d@{%_Ul63&VlyoZbHn7LZ)ZVD23^c+cOf}%|Ap{v zZ5ANs>B3P&;ns}!!uSA-6SYEC^`+c(~Zk=Y8x|J&h^oyY5O15BbE6(MKL9`otGpE`1OwoJS`LI z*UO{WI<0s_;`1A&HM8qK#^qTjedu$%0Wm%i4G>bNr&XNBhlg!Nrk;M$5FycFX9*5N z*y%UsGjgn)Rp?!DbhD~&WiLBBo*G6E+##+gRV$7lN1hXW`O0R#uiLmo&oTSQB_wse zPmBXz^1<5U37HvHkhM?xFm92WK=s^zSPV%*ZOnXfncT#e{*$d`8Om(CE(|gXA`FjC z2B---zxq5`99E+w$QQ<}5;ij!kGSwvQoE5j+hEOcsfT0pwc-08-AlyLw~Q@!omcl9 zZT-}=N95T0WH||>NOiBA1mug1B=)82hTCt6ewlc`9Y36WO!MM>`s2WH*lh0~bpMG= z?TF(<%Ul&q{I#OpSdI8oq}^!ixWz-zhpUW?6L=FYrjSR>+7g=bI>kM&#Kj@=SSsU` zC)jIItR_9b->0bM?PY-n^VwQ`lV%vs?afa~I1Hn<8~(@1Md=bka(YI7PNDtOmM?>0 z+UHubP&!<1w49`uvr7ar`gA0nFoOzF#%6OxdzA1P8vus14$rb+FaZqzB{8Z{4g{It z3h&aRclk(8zcR@^mF@6GwNuOCZPoQ&6Y~M{^{l})*jnXntZ|eSSi+pg$SIb|psuY5N zpY*GG^j7Vyq$YEjhG^-!2l9Hky_k95P;}8<=WM?pBRa0&Knf^MdgL|hDTY-j>9=`I zi+r^1F~KuJJBc%~mD|s;yJDE#FDH?+HunrpM{dAlc?3c@mQE+JYm`2;ZKOM`(iUAS zJeI#VJ@5A@9Et$5uU(nOsXbv=a$SjJ+y=|H$vx)=d|R3pC_U%EUDhyM*PM*R5GZb? zf{mN;kr%zd>%SdhmUb>a;e{=pVZ$t<$^Uja`PUGP_=7E9HeHI!-Z>K9oEz}5+jd%p z&E;e0Se7$a)2K5!R!T1D8UR$xp;`XE+pl$03~4NI1iOw9qm1kJOwo}z7M+IiM&i1W zrqwGO7HPA#^&X@Dt>cBgyv4+^hPQPbn*j}%T z@tL+H!D8|u1RJN8N@ra|0pso|^V=vkHQ5FM=hl{2wJFsa@=6?bkPh9)&WeCU3 zqkM8hIv3`EizZPfB=zfnK~JIRHpbyMD zB`WBV=qzMYzupE}F2MV~)?7QJJ?2gg?f}SEw}@w10>+ewPUChuEq&Y66_fc^3D=?Z zLC}W65zy$*U3vSSadrnrS{oE5o}heOtUBupKTC!+{%Qk8X}Ke=@BW>aSR16575O@gwQ4E-0%rZ3J7x0_v#qicPhnyc9+noJn0_*(OKH$t{Wk z5W4&Fm-ApHFCT^B<4{*x#8xrL)4N=X`D@w{J+ZA~KrLgoNRn{D6b6w>=r+JSDVPEg zaY{C&);`n+#H-69I}RnAo|q4!Cw)p=mFALR{J`;~KR7mAzoax1H97oy}9 z!+Nf@FJAYqHx7}nc#s}m)61YbhW#RKDpK@Rt+;Y7w;dJ`OI7D5+2 zVLF_;E(V?3v<0&<{*^{5M+wpZor#@aR(rwTcQ;PFk_~>M&yqTRS&*l4v_T`Jn4{+D zU{kGVFP0({>b-p>Qx?AF-gB-qv5L_fym}8R7Cx1psX(S*dJV5~`Ed(D8#YIg+p)vI zf;O5hrR{3=0j$P#d`@$PYaQ`uW6d;RtJSxwd(3Lt@n`9RntIrD;ex$AezJ;M!2Z3T zy(~B(bc?|0ei^{RVIh3zi*I~39COqne7Z9qt6^VbLb;&^{b;#utaN-+yA_briyQxwc(3Zh`Msx$hQM!#O6p}JYd9ifR;0v!y|yZFNRaS&Ybv{TPo0jvA#pVF$9(j zvrct>pBn7_M*iINQ+~OSsHfcO=hu_t%Wg`HsXIQXPf0B2IWjbo&uNj4s@`s%H647o zo3%BsJ)~|N2E}BRmBT|T4*xRyNP{HIymmt^r}l)3XF~ktp~6k>jVunRgagrz%m2K* zd~k;MpZM1J(4%_4LvTAKN&d9!44~YfRw}{bu;klKlKf6Xh%usqyPSK5K|D$Da2ylJ zEY^9mFOR%fVI5W}`MRc|_SB#s$!>VdsGs3>>T-2Gb+t?e+&<=P*KV5}tWplxNI2h& zOL=N6rWP$6&g9`9+TJyP$kfv~BgSI048Gm9f<#IH@{w^3tTz$T_^IXmdBnr<)!08l zuQz=Z-sA6f1Eg@t@9xncCUBQ*736hFM-+I4_I%jEZPpl(y1M8mA*keQdOB^^knUYd zwWwd1{hjq07mkERd$#>1fmSUik9*}b1qyESBrI7z$>C@=wN>iOw4xZnT5fN1nj{Y) zW;GX+`n?m2GHcW7hk)behvIRmB~fm ztqWr+Tc6qqfv&E?h+tOy;BB+!?Ec%|^eAWf@Dn?q3cTK(|6&Iygcy6y39Op_4N8H^ z3N`GDueX2sq$}pDqGDCy_=cS zNmZ-foo6}u_G$AbS|Y)z*__DDJU{QNw&%{zE5Kf8xSQ&@1l5HpY z=$>MZ&!PFa9*?niycyMd9VLf1&9eQ#({mlhsfHf6sv)D3npFZrR<^K{8)D(UF$}^0cRzF9rU32JdoG zRG7h0uF;Ct<}mQf1a5rH_;EG*gZ+>BG9uQwHLo`d0B_{1ez!0&D5aACMK^vziewQ7 zjWCLOyVIhcVZ(yR>+E-%C8SkF(hXxT9h6S<7~ODUhoyJ_PAmhtT9&h|4+UDf^__8y z)rs7X1LVkIq6TJk7im#u6`@1GB|y*f?S-@1#0~dJ+tC59JA`0csR5M3ehtGc_B$Qv zWSDcNG~*0t^$P|989(0mUi3WPSmDzjGeQjYr@gftS9(vRsBaWu?&PvL#rVaaIoI5W zkAH0u+K6%6i8b1m5sP?HGyZ9@MU|)to)^J?RObrwx?OQ1aU#BhOVW$x!a3>y4G6;# z?>p)aenX1X@XCqfJ-sIjk@3F$tY6N#rfDT`e=cfgvLHI5;8^0U_%DT)R|uLP(*SB; z9xzLloCt0{Sb&28!15ErEOu#h3@FekoJa)H19b+=+jnfa6~D+bF$yzCDoI`POzxt` z*JyB{=%kk07hLA4--6&V1E|B0^Jh2`_b7s;E>Mu;!+!ht8_so-{1Bn>bHv5Pe@l+K z5gPzAYi*lf4CnaKqfRTgD~u^W>=94d_&2M^{e*g_}$| zjI1!*gkh*%4X%ahU%2i9?jqY@JjUxNr5cDlNZ+7OK7_q~=Hcc#&>7ch7MO})QDBlQ zbBN)7*p3y^XhBtSB%(qRfWJCSVLx@bSl%2T%?W3$6{Vev3~rx(chZLe?^3)a{S=+2 zLq%I{%Q5r`IG8kfor2=GvWMw(HiBx#g43-tYfiSPGu;n2b7a4@TbnFY?Sht+6Y5{w zsc@&Mbx+|OE?exzI6O}G55yDeK8-zgocG-KHk--umW&A3|$`vHZ+$bIeau=sZe zmj=9ZcHl-;B-EJ9J@r-?gH8L#`UX~&I^&DAv7ZG({7Vfsxzy6xOn!XcS~aN}55&z6 z!jZ_n$oJYywPq)e{9bLB0*GdELYJY6Uf*vkUJ~E9ZHR84CT;Rt8aFZPU$X>cGFWGH zwJ~5Lbf=kOhihL!45d0KkHj~l=~D%A_B_yD_&J;82fyFE+aB5J?DVyZ+#hTUmNx=Z zZFf+r^;O?%T=WXwk2b0fyI2|PLk0yhWm8`k1X?`~DkL@h_hz1K_{ zo=;w1AZ5M=$z5@cU2!(j2%>t<4V+-$LX)N_0%4Zb;{t|*uydpMK}eYk+w_Hb)_xn5 zq5OvVb+0^Lr-YoHRK0HNozbwEe2f0IJOLl^{5%_|WJkZ=A@3K)g56Ly)(^}0N2cud z8ky}fQ+BANCcnYkd@?0g*ucZBQk$YO5_)THRmDS^X)Z>$U6EiY zu2mIZK-E3?MR@1x38Fi_H`-ujWbFiJ>KfhkZjkoz2aYTafGJIgF0rYb7|vM&C93&u`b+_a|Jc`G)8AWKx1zECbN01b zp>qEC%0H3E@WzjymwV>9Am<9!h_E&p%jV)fdsf!|YVq?YpD z!pitg4Q~nESxS3!tt(K9P^zeq}MBn;GK{H@~>mz|Ze zN1-DOoNMc~_>bB^L)#x~r$DyFa}3qHjSZA}r7CN$%UT)jlAxAc^Af`F}NPgiP@s8ERfDCb_r)TC@uV@2Idswu9AZp#l@*`#K_%X3UldDCHF;>h>dYv<=2bONz{{-~o9 z7U{+Q;6fHrfjyaCBS~ywDDFRbGR(t^?1)bfMQ{90a2Pj~nn1AxqVhIzMAy?dRyu_h z1Wm?YPFFUGjBOSAs2g{SsP`t(yM?)8(}}KC39CFF$T2Bi-7&hCYEIU1iEa&HI?kth%XPFN;r6bva@iX9NabV7g z_-qi?bva@MQ=oEt08t}9B0Ed4tDbDFfZ{(M*qZ$CQ4?Lp=4UAt*hZ%Q-my{6r?dJ@ zAY2=s4OerT>bth6&Q!e>iG1G-diGQ=n~!GH=tF-428zpV!!2z6fX?%CKSyjDC7w%dea4Ll5$hN*8pkOXGlGOhm#} z_FCUP9&3qLOHn&haH#(BYjH)N`#p~D;)fS{*JsAp@!)WRw?9)vp_u}kZenZxy&{X= z^B)_L%8bheBEART_OzRFmobRTC z$=ax{F!OGGk7+Uyucvak_6!3T&Vqaw0qAQ>mj?v?hCf+xNGgAvxCwIjNr~BYy|Y zHOab_mwN7ZLJBNbdRn3d&os-Z`FIAet8U;>#&Vn-rVjFs+H)d;?Rw!Zg#ZWSa;RvS zYR^Z9XGJ^imRjmX&8lBcP_Q8&h@_me5Y@B+GSq-Wt%Mi7TQxb-gFOBV~9SP<{o zUQSQR>uni}aVIjwW{UQ|+Hyxuwhxzit0oEj+WzFd5X?wNqyuY-Ca&TP8h;(` zEVzTkznsuzkI0xfLb|mC1{gBTovp2}>{hm8F87|yrch)wet7Ul^861C^@dGMCPVpk z%kh>+S83VbNanBr)$$x8}Z-z28f~{1?|5_q^N>Uv%>TRG2-e{c7 zeD~f(Pf1RNosBcKBepkPpwl{r)_}S3!#Ea_rTERH{rF%WG!)mnCI)??M2cz;9SpgM zBpE0KVa+;mOuPz+?KYs!I^Zy|K<;CS=q)39tobmhzV8tmt+ zEe3q-gi=Vp$-k62&7~~rgspX7zV(X3)+=m;9NIFL3&f){9gX6Kq@H+VEPn+;cfcsD zLXV5L?%2xmMUf#J=&(hn`g?hhRa0iKnbRuXpmM2TMw2e-t?X|^VnS))0 z{b=u{H(U{TBr(~ zFO+mpFvK7YAQzVbbw6$c!*^#T1}2CnJQ7haeqm&8p>2EL!)%M!g(rlAEEWh|6N@<+Yl2&f9s-nkQc2?qP zt-yh9Dd5<|NI+m%%>|nAOYT|Q)94RD76^nR*8Nj4QSH&RUBx4{$+aIh4pOEmuGq`% z(itqUZ&|h0`c-KZLu=eESOf`GiW;|Fv!A*#j{(9yNHNu*)~3@sp9>1wFwq$w?Lhv* z)zL~#TWpzWSBqEXL_hG&JpMUkiDkf9sKD6S4E3^a&Q?t+IG*fAV>o*nQ4A zO8Boj;`b*YQ?HuiZuyFd>kXvGMb(zFWYdCH1mYCd_RSx4;%M25j`9iGV4T1cRJbN* zsj^e>kMgc_?5*-!W!6g3algnnwf=@#F6$=;onVpe+co;@Qv2nkIIpl>=f1P+ej|{` zXq=7F3%|n|Ew9TAB08?( zEsj=@R@b}6W%O^%=3xWQ;RDVE1JTh~)=h4tmN#J=O>4j8FH&!|w0Mf-P~q})f^4*zN4sjb&ywk!gL>rn z&mqDZRdAYjKTIldbvWDs!cM{%qzYH16Pl1Z(1d68+dV?yrgl!}ZV}fjHL>`4>$9%c zEL|?v{DB0H&bpfjZ$04itT<%><(%M7RaL)@X3NQiLsmv4HKLcUBCA!eH}q8F8&?e~ z;x2!@fv`3XrWDUXgrpq+*_=B5NcTQ&%jq8hK$Bp$Ao2%2$gJn=C50w#@h4%yKMt&m z4RBq$&&mr!H5Dk&eN7G-o>TzWG6#5iqdD#hT9CsZd0`3$AsUt^rVJ~d3}6bPIW@Dh zecs*&2hBb81-LCWt}t!{P#0+4UdGM`# zQQN$kn@d*F_Lv!D&54Y)=K6MmDaN_u zrE~AN=+rP0t8dF8R1YD=H>k^cr8`XG`Yd1*yrHm^2^3@; zDqc|a^F8kUy{}m#Ls;**=8mzKXX;gLnCQ8OP`$Smx&=nHwC$I158*MWk>tTpa8O_L} zZdLf-z=0_q^1+JL1wmriAe}D(ACf_>GKiS;v$We{PGx7 zx}X~jjuUI7q4;`%)?*emUtRh-?NZ1aC{n1fRlRJXTGh1f<40*{K0H~VC9xODuwVHv zRMifJEZ^&K!B~!#>67FjYJ-n?hQ$*Efn*0SAjt>JTsiW2gJ2Hy`a~3Wk4YUSOSb>m zjfKs^C-pKBipRXK(_)g5YH}qQ+lFHnvxvI)-BmA!dZZcY%Hv^5BGdX?t`TuGm~QO_ zVpBQ@Q^n6o|IfUv$2v|;qrHcnkcvx}mT-yrS7IbjYNQi>0G@!$oUTr>oOx09`1Z}{>Q!ZYc#{QeP*+Qn*4JHtf>r!odPb&SKp^fU*kSDsO*fmH1ggp9q$y@zrx+KKRDc7F9=2UJY56)kk*&y?}PNtQ1myt?a?e3U$-6246Q%E&Ssc7Qpz zKY|VP(D~l&ULt+bC6OD#GWHg8MFZ3NA2Wgo8SeKC_3Q|CSVCsOuV6NOb@Sw23=@} z@lx+MbxWv?UqjQTjNmuU_Z32R-LmyfZu?A8xbUr;x?tQM?dw!m6?%82M9sy8BgAJO zSQ%^OQspU1CA6M%nn!e(f0k# zmv~nt&6@(`d|WR&`bE1QPzU6mLZaLGLbZb^Ohu_HjQ*+fK6w3^^E|ke;)QyEn*D2t zZsrSHNPxCl6R!eh8&dFcGyZC>+mrHN@J8QYLQ>Q>jX+lxfQOcd!%my1tEt8@R`99- znGfY%fHhk6z1M+tNm|y|7R(~q3@yW5?t+ylXJQ1JE*(R=?7z}z7QQgp?Q<~wefITZ zXVGNs(nC1z@be#IWt?71^c(J17}kYh-wkE;?5{p>#12&mKqcDrsBS=A`D{;bZ9?s8 ze|9)};d}SyLpjqCYzeLJ(@lRM(d==zG*@eS9{QJUTp!nT_<4)ULPmU>ap43_87_Ox z8Auh;ns{#hak#?IhTgr?*fpKW<)C~yc%wScgB{28S0?&&p(3kxha1<~RyC8?mB**d zZ!WZ}5`=Qec+F{zC+Y)D-4}7)pgMdUgJZyYoEFT1#ya|dTW4d7eONg{DK-D6U-RPQ zL`he4yr$#$O}gi!sbwejfZJvb3$bb(thXwT0L0zKedbHqN#7>iaT(d=-yR9U*Tk2* zBk?R=KNR%Qw{S_#5i6L%UWF&<30YIUhFMzDHHrR68=TdAB%8<-JVsMt9LvSLN&;SR z^7BjLX{Id^rbH(-3P7hlVb;E85sRGq`eu_S>!VCA5jspq%VmP*pCTL4x^oA{UY|Mk z%yqGyx{JpeHa3s0n!WIBp`Q=74(fVzg<_u-WY6SD}P{861=tXmJ;MqyYT>wkrm z5U{JVc?vnKt+OM9y_8>rY@srFMR0$^K#hBus?6U||0kfZ#CIKg|GHjvK-{b|H8Wk( zXj;+&mQZ@84;bcUG)b1eTv&{F2hX0L6g7W1hZsbyB`6U|hA1Tp?l zjE=6JfIR;_wm#&Qmto=r1Cr{7@x5HF&7s`id6E-vUXchAK@NV zr$47V_Q^mkR;gV4N%dxrNAp=|eX@2D9pdaM@m|pPK0GgFopBvsciLByv8C?uq@@?bh!k%c5p~5liqH zP=nnBVb)tZK;+c?ekulEA_NcnAj$40_@kJ3(Gfb7&~>LRu%+j~#PUlJd!$`qx^!qg zKW$MxM9R}>Ki}12cYqp&3u90DaNWoII4b;T7bevf$zdKZr;JiYHfn-yHj*rJ}{gravbI>$MuUMVa-GWVP-phjM7Ph=5k zUIQx)%ob4eq0o40KiM=Jl{TSRy?HF?VLRY#9TGV$D@d@jo(32<5jBMP;6+^e5oLnR zu?oRd0P1-e!NQHg4^2pg?uVB_Yt_1rxbc;RkA`^h79t9lA0-4-!+aEw;M?|>MQZx5 zxPdHY`BDfgzW3Pmxm5FmWV(l`{&Ydm=Dr3+W>YKqEP^NReBofid)+ltYxuv6$ zeXl7p#o!o_PKP=dVVkjg{UX59v_*pd@Hk*L(Z|8uDBwJRpY4G!9;HHG5C&Yo4^;qp zq~;BI5N1CD>8BGND!m5cz>Y2|cy-UfmCybI*-MD~p%p^JFGQCvMx9f%h-F=9-K5pw z?k3JLEq`&xWb1mgrhyO9kX`5^@D+d2ywdvvSLZqbyMPkH4FF zoP4noM(iVeInn#6=8P5hjnNZNN0+qQZWcPU_kmT_AXZ8V zUQO&r=7}2#O!`{FWJ|v22)u}NLDJ%GQ|?}ud6H7mIScI{4{!&-Z}sGs=<|mWN0Pw* z>^g$$@EoynEmGeikS48|Z0Go9Jh<1~&bJ{4L_Hs`@+u<}-g zDsEGBbe{u}N@-0a%7`F6wXECZV^A&{Plhmwm`2z>jS#03sOhVi%ZrxF(L2J#>QB0gz-S3X_6A2Lq>Wq?F{rtjsl z=8FNL&t-5Wr)-^=mb>GG`(1eGjANKE3l2jFxA&hL>R)d#nmL|2Hs``^4?qU3j;hC{ zjiVE++IC!dO>_iRKzD^7m)d%0C7Qj&D|ay0y~zr>{dq32D@dG^a%a4s%jM9%YqXIR z#Syi!)YuA_-=toZ-#F_jgv*fOb9%_ByzRl-7FxIX8;o@|KDs^A`da4uY`{hE;_kto z$3<^q3Con@sM)Df^$0{+B4%)KzKBa5so(D441SKRe_MK2Bt@f`9O&LsssCSA(fGOr zLyt8^my)(uwCnz$Wt1PJms!%pa&3cE0t#JRa9~U?A53=o+j9HG3jTzxN=%>+f(=elHuE;?I_EYjQenONs09fIQ$*ThVNG$7)OHG#R ztIHlm=}^O zK@QdjCL+OfJ2g=mrch#-woMMLL-uXYieT1h1?cju;PEWz&FRdn>#oP_@YgNd(B9Yzpf8FG!zY_l9B-{**cYtMN^(ksIm!|QFh&C zqJi{9xNw>Fnh6}4Pdg#Vbfb0Ogs4Wb+|}fHqPdT^aaH}UH3=D9_*uuOHUF0(#zDEo zU`cB4;!Iw6F7uC}#q^(SiI$JvxUq6cpa4Z&IaEmlkzx=v%$t8`ARmbNpE{qWDHw+Y z@U!YyxJx$qSbzDE`|Xm3)&L>WQ5G0%G~N|b|0T}{UZNy;>S{qURxt22$h!y0IMbwE z{P4=CIZ5f#CzyqyL{x2598ONDtiAE6e0qgxHJau4t)8@_Btg70rEXF~J*hnxB>8-h z^NA`U9v?83o->xa--q9}`q>LFv#h!8R)k#lo@~oQ!PfJ&`fo3HPJ6vC5^;>LgXAxo zBFET*y3nCfysy&m{y*W&6XEpG390&o2qPEY#4Xk)a95nCEQ}Vf@fCl3S|Hhu_eJ3u zznVf6?yWLxGsG}}-K3-+6`7zLZ9mCOSfdOR(7V8sECqfMW4UsOq6>~aQ}@NWFGVhj*b>UKQcHSJ8pbYmovZi1*>~e z$b0zFBTEn&~)&{7f@@ zfSs#&Znrdebw2mqu_oc)^v1_tQ@N`DS;bI`Hp)hy@B=bHS z{CqzZxmxedYyu~Y#t%#`SRZHtcSD>KDZok^B!`rGb=)L?M2{TRgE*Y)?By`KN!cKq z!}333F7oyZ?bXTHI^{7*ZIBfwg3xoVe>FLs9oE*WGy2>#u;$0&8ok}9=y@Y=ngaJ{ z)c#1fo-}6>2WYVEjehubZC2zk;;h^dr%Ki~O)8SL^g(f`y!sATi z)ov=?TkT^UiVC0LqwVz+d&y7wX-uyDwcp%?^od=6Q`P{adn-MQ__*$<46KkyuVut& znK|xas#P%HVa^S4j+SgXGIysx}o#Uc;sQEBUHg@op3W1mwy_-e4Z6ZR1Rj(>_1=T-=FOhdcg^l2l75-XOd( z2hZ{1T)9Ur9=(PdX@zYPS){uo(&)D;L%NbQ?zQ8#{?k=G6|Y8)yQ4o_PL(CdF4`Kx z(__V8*I0&VoaUIv^!gHf z^!JRQ2@!IipX$Kp#8Z45d5r_Ft8kT-7T<)0i93F7w{Iby5=<#WHO0)A@LK@e!h17d zDWf+HZKvS0Ix}V%eujOc=EyRI$jOe5cKOjBR<)+UDG=emcB-^Zfz^*;#k!;cYm>zh zB(?HFU&tbL+x{93SM#~RpP6*sPk6)T*{>>$va=9|{_`$&Y0gbKZ2)S4b^+nx1w0w4QVr`e3%#3V{h%J<( zK29Up)J2~GQ)eDV2rDo7FNd}L(7=jRn#~z65rC-38UA0$3WC5?i%#_Fsh@|%wuYcxKyuY0agO!QwCYzfvD4hGYNSrJ;SGNa*~l> zMQT*q?qEu-!$CrZ3f=L*`26|*a8746EWd&rx=nYQmD9XUCTi5A$3zNM$;M~%(F9Fm zG&EdORIvk6ReQ7r8bc zP4zeZD&>+FTl*a{c4TVUKtE5L&jc6?-DlObuI+_HLzA%n2#IO_t9!RrTvH^WZaCq zc@GqzP*LS<1#nQ{T?N#j6Lfu}3f z;yjG1th!029&5o$$szI{>nJ5*k#)QEQ+(hT}JPou2{v{HhWY_-Xw47=eIy6#=QfoNc)G%84d$A zt`;YHj%y~%Y1a6G0fnb_u9GKFb2xV$3O03Yo^3d}K~%ZA7}W`SHNI2w`lmOhv$tgL&DP2CBFNdv+FCoS?%e zTfknYhz$t}HOA*N`AwivW9Agh8PJh;?JR9gr^Z@2&-Cjh`ZAx_=Xv$Iq?#Y_lHd)k6H8>3c`brJQl_8G2ilSE-u)jHH zjrq2rq9Fd7=!1-k(2tplvZ}w%pk^Zo0DU9mNPd~8co^rlg~t9r1;dam<0b&G9<4w= zg_t8qPrSmIFtye!s#Qqf@e2l+oqQ`IN-)?Xl0|CIowL|=H!~kP!=cyQ4ioM40;btz zO`GpoBSJ$W_c6Y=lT^bxM3k#{N6%JpU6tVMHNY6%qwqMk%>O9%R2<3~c?aq>9bIs( z0y$k^_q&;z_T80Q)n{tNtSp42H?!LX=T4^`>(cj@hQAD5H2?8{nnYiT6O^TQEY2!f zdOYiI?3)+g<^P&3pp;_M*8|+-rH~?*UV#jSj_-#|qf_DR)=cSBq0v6}ZWw_2NPiL9 z4GeO*PQ+6MFljW+FV(6&N|FZP&ZG$e^Zm7NUA$qB9=*MVT+<|R3S6vQ{d%bu$7@m9 z-D#v4_XjI2HVlj?0y%d=>W%y6TidideJsamsXdK_Zyt3ku02|Z+(kfCQqMGhsrMBe zY7wxE2cs9?FPL>tIgTfu=Ed!Y-hKU=rUwDG*%(doSgh`Jv!BrGSsb#aPzB#J??zuI4=GEu+J2P4^uwp-d1$6#X9X7 zg?Jw;pOW;88`hI#U`Vk59p^4#5dP9#3>YPHN}{pX*m?*s<~ zhUi2c2y16?IGnzv*My)WP>F6_Q#2|!xq!|MszpCIY?jt@CXJQ+q(PIe?0|N7QYbza z(NT?(d;BDL!6=gfiT}Y`S#F3%8jyKhy-Bl!uGgTPM`+35b9T?fLa+5upl+uATDk;` zyzsKwOkx$z2V%pVRD|!ut7u0%YN*r7GeZH9+Bz5l@urGSH}rJPBbfqAV}4=4TYHc1VhQ8C4syI@b<*A{L^MfOT@JH|6| zOd}qm)_=9v^^a|7fNCPHSHAU1s4d^VOee*BP^~320EpY1dO98`T zZpdM}xEqBbwelHvo~4@V%mj+7Egr_P(mvRto;}5HN9VZ^0VB%)Zq2Ga%w=`r$$g$WBgF<7vfIMl35OCV?iDwB%WWCf0jUXGVPd#vHLiMT*N z&=G_3fW|_$aF~&0KKX;(*tYzw*JKVAJ?AEIZn8u$Ngj!g0hQoK zmB5QkL~h*Iac)CfY&v2KA&lR#o&>g+KynW3H$<|4a*ewJV?R*3c|Cwg?YuyZ%dqCE zzKAu{uWB9*jQemtPY&h*$G&r-us(@wj|Tv=S>BPz(tzhIv*`~G^J~yu;h_D-b^&$< zagK!}P_&U+_2@>gg`9>6jW>~A4aa`?YRND$ou4ROyJQ0f-iB5G5z8*@MLVP#kxogH zz$ZO%R~1T|hx$w-l0VkA3ALueAHsBzKg_GOS+X$PfTBP7Qc|*b zmhthMBQC}ZLdJWbjvUFp*Jwr0dBu%{K7qK=T_V$n10Xi#3}LF(Tr z`cpAau+xT7Tk3UObf$6~zcPQfh;jN?()P(z<=Qkk`El8#6Vv6#Hb1-UHzgh=aRF{X z@Wz7)ATMQh0q;tD=F6F*T^kPI#><{r;MR1Qa%<1&0Bu&J>}NMQ&^t)+21TsUUhcl` z`^@PCG@FV#9a^2E|83%ocug`y#G4Urt>@xalrV*i9EYpk zy&h7r9YS>6Y1l%cq5TMIt2c2;;Ghh_Z8nEPtLE4yVou&=R8n@JGSGzr=}OkgNVne3 zON!?A>qb+n^Z-z;jO!G2kRsq>gp>YOKzX9*O&;Cc^^B7S(z_W%ES^4%0vdh&A}LG# zBpdwT1)df#ZBrx%Z0{R(TLry3aW* zf;6ewKeYhB*=nUg$7^BTdpG@c6y_|anYTZ^{a4;DRQ~AXR+mj!Q~K6=J+nBnFn}~& z6k03Lae+*uJoZ)plac@?zaFVeFR9B_lcYKdhS0or@=J(r)OIMTefvvr>ezWaT5T{e zt5gw?C^==El~p6-@qzVzJm#s5CC5gZr>VO<=gje(5^xNZ=P8c;NOO~mnZ*^#z})|8 zCM>IBCXUtb4K=!;#Tab7UNnkuDs|OLtdZ#FDfBHqH9_cp7zG?8yE7yS)FdQpIbSh_ zfoclnBo_yfq#MD>$HPS%-&az$sp&_8Q=qivY5vl$x&wKuj@*Ttr59l z2UhezvQO>zfKJI`2LE%wWU+z>Xk}Xxk^6%vP2E$devI5P|8=@cLI3CF z)%|5L@P0q{%-Jn=@OmMB*1*@m-v#+QzEOwz(Wj0OLzhqS~CcQr8*Kb~XRG z)IP@p*MJEdKN_T0Pr_`bwP`&^zTe7qkAgB*feFh@iWS{;HiDkz=#H2oIE2sEw!11{GiDzyP(g)5$z;oDq{hQu638wyUoe^s ztU^~!9|?;7(fc5_J!{3_{2JyJRo{O1fLb0rt@oR(yUnNj-K?FZOHDp1O#TQOjY6tm zi$PU`VKVGY@r$GJ<|4mo<aj{g5CVZ zB9Y~@W-b7qf!WHIGLzDGFT+;YMn0747)^v6Fz2+(Mj^XnY;rujF0ic>pVov-pHgY5OA*XME-^dGJ ztFRnsiQnF}JaupIKH#Dlf#C!R?KKVRXk93;vV zH@0R51qq(_V41U;QnJKj$^nkwy5TaXRD@1d%@aI|Su;F&ZvQqo+%iQ0lxrL!cPXUr z1m94sJc6RK6ZWZviT*DM1DbzRu3fFnJ5yI2sajkoGfCyLPQECBI1t{rX#F&eE-B>P&z;oeHZM^6jLeNZKSZr+u7$IitXa-w(oCu7oSQsrOW^?8+g7 zv-d9|Uj_23)&3&ihknwc89U{!#I7hNCsm3;{Z_ujaiNccvS-olE$_lmn;<eOkrC^BM*aV``QnkALqew_536_!A=L6C+grXAxdN!UO6dpuc7mk*E7vD ziXY{s+H+YCftDa)>xB7P9hY3e*s7Y1+UR10Yxedn$u*e@QPESpi;XaRKo0#>M{he( zX2_yJ`xsAsD^^+UZ35~On*I`J%tSsy=5j#r5tU&GFJ1>OjH5o$e0xcqJm6w0YT zrPNI#3G*7{wa;{H7+uhy3v1}aT~T|Ha3Pgu8&PEF>SxL}me7L0_ZG6_Rts82*3E7z zkM#QbF{KZow2iV>LYyBVx4MnKOy6|#nBw8FPV$<>jXp|$iwBs6F*1&x%Qhx&miBn< zOIb*1smxo8MnlqU0GqSx=P`j5|d7G(!Ejy@!XGOtO5m-zUdPTiOfWT7qc}beO|(|(+y=Y@%y?Mm3OY5XMw+YQsg|Q5c42b1deD0f zWV5c=XSY|4gPbBo=AT}>S4cOo8GZ`n1YmzQ68EYX+A0dyt4Ok5gNbZ#>w6z{tvL7= z>Qk1My9q#&ZH_Sea9UL(r_WgC)~TT(W419e|8VvSHcE#e3J~1Wn|B59*=&yPJE%4jvzsM{Tn3W%MYc<&YZcVRCl$2^1PPF}5wiiGl(Xo%z zR?B0#(9L9~9JRuQVOI<2cAcb6$X|#fAUqNETs8JBboF<=*TON)41<;^axX(>F#gIs7O+F~jd5lo^j?zAV*unVYF=t4~}T{mMHP zU^U+(z1cg+*uzx)zRnAxf|#+J>+kI=uMGDa6SamHm$}5ACuzrlC$4z_5~#i-{2bq! zVB-gPbbHo?2|%A@ujiw?CDm=F`dM|=_jK#=62aZyUy6ou1kuN`_yfn@84N01e4ZFZ$qT zSi*VoiVG%Zu)`})k*v9l0r%UM!L>BsNq4E;m>qjDsWm8%z>EvvYtS0*F00Kzqw^OlSRk!oT?b z;Dw7L{JhJ2a0npi(FbWj^T9FQIuGM(1aig^i5A;bhd8uw0OYLxD}-v`1qNAMHP1QW zFn?bn`%|2jrtnr15>@Xw^h>owqdQw@23?@UP#Sw)@-v`h^U(Q)At`y;c~M?k(L-nI zS{k2`SeU;UBB+v9*tzo&HKYP#&)h%4Yp+7!g08gdjF&yB$Js&vs5vhY{}b(`cz*89 z+7Vd1^EscME%=x}q?uGeEsF65^R*v_@ZH!Hpgi9xE7PGEDFIAz~ z(`Zs^H{?j_MJ>Pkv)DSb?vL1W&$!%Qn7$KFUa!$DPT2A8myv<4;@nVLZJT2SHq>*A z!uJCQlTprX5Zx|K#P1BH!RyPPlN?9Xn)?&^FqBa0a+(~^FM>RPJUQtHXi4aWoEb+3 zkYD)eQI@kMiQU+38;+WXS>O(@OoE#zvqx+(|zl$1(JT zy^L{RujE_{9l6^+r_lw|&*mgscS`gvS-D~fw%-}vHtrqF-yryZyrfgAjSnf@KE;8- zu(VHd_GFSYF?GBL{pBX|X#`gp`sp-@_qCc+UZryB2b`3PhW4m=M1R3Z`NiqjTO9+6 z2Bfg&d&}a~@^W&r0uEsdF*FKKG1Zf#PAQ!FblP`XPv_bS6?oG+UQQ+E6L~QS?bxtO zT9f^6o2rcgp6){<${W*azN*P!oXAQ+(QVS^C(bPbhs0_gZYQ zVPWIXQc`X!3ziRw-(f{Ukw95TH36B6XHFL7o>rV3b8}umyTG`@Etj{O78mh0{+FK<;q7F|Vdm??NBAKGm@kR~(kQwPi8O#9U+Lny7YTfd2;3wL~ z!*T1zlXvR7Pl$Yb@%)92jxVZX=^hG$5pIk%0;$K8A|mZAC73~8 z8uZ?8x1$cfF|^$;Vy3|g;c`N@Xqn3!92ceWwS6RfvsXBFeB&qe7Hu#U^F#2A)j}hj z1tLQD_h$E%gK4JFv{OSMA@P`|KsSoEFVJX>LBF~?eD-PKn)p<6L^(4{5Tj{(qD$xa zgvR5MxMrt&Ym@qZy|$&WmPA^Rmua^<#>dg)S1)6z7V2>Yp+hWrsJ zBbmxYuvz`Gs#)>_6&cCF)p%V}e`r%E ztC$q>Rb4B<6E4tQ70|ZVFuBEEqZVoITUf1;At33DV%b=u3SW4uD^zoP`kl|};Jw39 zMW}Xu%hQb}56jP8__t^z*MdJ*KS6+|+}Ifnfeqw!vfYI`ab_=n9mYV%_dNO6#O^Ve zj!I^=g9!p8?UN1Vr?5zSHj*OJ3tf=rACFyHFZJnh-aTQ0?(rFKyW`p8ebXUHQ_RBW zY!X|C9m|Qq+)>`EvC2?lN}tZxow);}l;K$K)WkrF#w4%1U)B%qOnMvO)QM}WinHv2 zBfu&Q$0jD;4$G*7gW0IrC90$xT~16a`S}RFOI+?Vj?zasM$7jhLYcH$SIZvRDpqzu+i6usy=RYeBqC_XGTFcF-%}&PxRKi=w(y*H|s2{ z`O&WSS?zoJ7u%rSm5Z?sD`a{VHD?DVe&vy_>XTrQ!aKrauMpwyVd#^)9+)o&U?2HL z+W6!<$*$?EYI&O%|G6316EQ!R8N77VomkaRw~p{$k3JOutVI&IP z73=V|7hn4`+nanM6^ zV%6shb?ioj!VwhprAV_-A)6$GqTHdVaxY|K$%{T9n8H&xm&FV}_$}jpj^3~nC-0e$Zi7-dFt7<#p2IRVN7pt=D zR(z%A`l-9xbOs)4do)TsmBqgRTE)`7HrAlU?=O5;?qzT!??q&=f}9Ud56G2hoo||V z(#!c~-(d&g(L^Y!ZwAr{$JUG%cqMr+p9_@g^U|P~xLE8~iVCdf8|R|&IJW-zM2fvB zgWZd;YHWO!RqZ5cc%}T=9+kGE0?K>5JE@*@?9K~dG$pGj@)6eNeGB%z$>g=@k*>f& zH&6T7yuB~^xv%d<^I(n-gr=^am+*OR?9akCW8#Ld74{LX2vYnhc@~-c>;?L~6BZJ0 z33Qh@*W7zM_4;7owtC6EEa5fbMvvwlmSUPe!1zl;5umKiEx=$b0qUK|D3#JA)8q8a zSgHzd+M5fg@7n0FDp3(VcL21zy0^|oU&qtDu3o=Qo|yz?Tq)7Ni`Wy-v$}$;iPWl2 zUx+rV&20OEp%lr8_Q_g%{Vf>Vg>m2$HEO^ZKG!whp&%_syXM(2&&@1osNt}b7~Jxh z`G|)1MpWt3ZPiMOWK0g)jhCeP!Z{0yPnMut7~hOLEo?gl3VUmWVR-S6u61dW&G-g_ z&5npAZSm_upsO|Dmq79DNfh8W;k=qF?~Ah4L!0UOZq71@@hS4d-l>wPRn_&^-|r2p zldN>u_w3)ojoN$b1h7v7>K@sE5GSFZR3l!b^6jxulETpp<&AiM4shqkzk4M+Au`#zOvL`*}H(wFKRTc*%?Z! zb2dv@#o^aU#clj|7v}v)=A(F754t?f0d*{RvJ#P}BaT`Y{O%Z9Ed)ba1pAB-})W z+Ght8Yt}{)vTvC?fVdW5Dai>kELbuY#)ycy7LHod)W@0q#Y#iuC=^)1Hl+9aRErhn z+t30KZOiR^i?YV{{U3Q46s0z8;WwhAzLEx*4@f;Z!10cMnf^|QTeZCn-61rRXQi)u z@_L$IV2CM-?0R>t8?1nw7}B~ zat0&b8^!pTjHGYl4a9)T@TJwa_8)m}6|l11z9-IC5d=T7|C*WZ7l^aa@Ql_0O$wYg<+@p( zb;g!V$1yF#hKV-&J*Jpu@yx0L(uQ@0%b{p#)(tYz=w26Z4!dz3B%@oApou>abDHTX z2=BvIg-qM+-KPjE+W<0;4o#og71(LI*ifZg7NQX=V{{6m?YC36GVa`H**<*~pj8M5 z?Fg}gPgWrsm%XPx$&Dv1vx4Tcf4J_0*Uj%06Q8NB+v&wUG(TTw1LTS6d zBP5{$nwZgeSYuR~!9bqE3L+!m{VD)BO7vivPi_QM=TmsSLD|CGvyRO~R4XRafaE@g z=QZwaS1AWjyN-9ADqey~cBzMve?Z z!B4F@z6}ja#nWn3@fh96EQkMfG4S=GJ{2$P4Ej zvyJc0j}90fQw@(#23RM03-xm_Y%1m)S*P72@Ff{0LTT0esRnP~x~=_gmRjcte8)&f z{lh>?t&c$#64PMTi4&-A|4v%=BZ=4bDs8-#+Rva`@5mAxS3kjttNH5FPqXSLn?%7> zEi0nFtI5So&j$>(tP!gey5?HyPqgyd%06-*e$aIiKy?@>V?UNe5OOfDQK~_SuXe*G ze15{*o7<;0WklG%jHV@LnuXUBLmnU@F!!Y}Y{PWQ%1jbmTJttqeW>dY?GLHkRt>P40olyXRNeFK zHePW!SuM_0nXPq{_hK{tcFKj={EZaKmPYN;#KE$B&wqAPmX8bS*)g)8tBPCWq9n}+ zCy{S>-I_S|BlY~?!}o+dGD!R4q7x`-M=IP9$Kp7j_5Y96fL&&zwe5Ux9!p;e0@5WDulwQ$_pzcMN6)&C-pbOisP2?F~Il2-s ztRWV`bS}BySi#NlVuM=`(CzA9kKl@kx^_L6QUfsyz#t&zs&RwtinQ)lV@W}&kuX=g z;a|&3xNkB}-0m+=5@k5b#(g>B>=*>YKJOU2MGdzS_-r*(kX%4izgoxLQ*?NC;sqjQ zj5jB7EX1vLE6lg?V8Ecu@A}h56iSA@g znv8SqXQ>Qv@Znjxhu76_Zd8nL27P_w2SHfRW~U-;)F?jOPL{K5x=oZl0fmzZCf2q0 z>T7(9nk+#q&4NqUBD%Cx*Zua>J#?|4n{+z;{HBLXlCumAhAHG0f#Z7}7UZva`n(UA zO_v=?+KAV%?Mj*-4!=B)9@yV5LZZ`mUt_2exEjQ@Gu5t~_;+u<^S%q7XYLU3Ia|?h z@?(SfOcuN@b43OD%y21*%HF|#fnVc$oFX(juwFtyj?eP^&a~xh?dg^`v*Z%T5S^6f zHd@l>>6*th4`u!pF_GB9n8wC~>RCp}5p;4Q5+;KE`$zQ0hw){bh-*bb?!K$WXZT)x zPeZEr;;WrtXlWt*H&#%khee$+WKD3yipHpyiUM?>rBY3CjR|z7jghb98Rsm#{e+9Q zQPkg06H{iZ&Rg9I1$ZB~!Z3d7EGMyNi@>h16W#MeKuCG!2|PspQ6{qSy;Cl$YQ$;W z3)F1BO>U*7Iv%{<0uDGWihfqM{pdc-?vus2Z{;8bT4{d_BvJAhUf_EYFY^kVW-8Hl z{gmOiLM^Zr+Fp>q%uCDZKBVTt&glK9b5-pZ+Lgp$WOgM^Rh{iTMyl$6L4EvId$JyDQ?UD%EARCxHSaMzMkiZrmadFNv-7U5+yt2z@Jq<_LEPp9r6lK(DJ7;)2P z+)ymB+XaQ_dEmR=lFKW&=R42VWN-wR^;!7e8Br%q(SQJ*j=^4@LKY&!;r+u)riDe9 zMm`*4BEdh3zd&WAfq|Qnr-t~)M;UkR#jM6063a2G+TE7+xwXN5^9Rt>s>uDvj%%)? z_+v)nH>bcox`C{&m)?Q^!8pCEfsG`nkKWwd6Re;zJ23WglU_gDSd&P~Jjr2eONXz4 z&Vih7n|5{c2G$o8Q|@0q2p8LYHG;^5E8=C+9xHw$h;myD#hYa95Pa@H89^hl8+b5R zTgp4c)OYJbD*BKj+w?0}Cz{7k@-ndzcDkzvpCFvv;pkjU?H5T7JmJ?3G_=l5KSdyc z@osl_bOi?gBX_x~lZt#{^l1L@#!jCkDmVY65h8|!TuK0x67RfgRoD=#d063tm2F8T zu&X4UXRAo~NNBC*VqEMdg1Y@;ZbLH~cj(%wGf$P3I_$bfgk1$^kjb7%q?B1}&;`C~ z^@e+t36bH*!kmhqZT&zB=3O4u`dvOV>Y1PAO>>KB;$k(q@)RhYKG~S@4s=nvP$jV< z!2*R1M}Y1<=vPg_i` z&ZulawV7`1qyigl<`}&xrOZWw#cR*Cad=wm>{4;M-D2V9B_pz6l*ZcQH8O1*A=OSZ z3KZcW)J{tBj_`Is_*BW>hIE2kD=tEkUpw~k-Z|Ib)i*LuftdV_@XFx)Zn}+BGdDjA zA0GC)9SNC82i^W1ty|vq>4#GflXyXZOfWBg>)?jx%tR9>`YTkDpH1)L(pS#+ocgFv zjt%v8a*YR-@wCZHkGkM{z`fM&NZ}5F1qBK0sO%i-Z?tNxOi6G5cC%LRCy7U>3;5NB zt&K~G!}J2k_gVcdQmHe`WuW5X^4@vwvRJ>v@GbeOL|S0d#k;1VQS3Cm(^|5c4n)C< zQ(RR-M%c%|=a69;-gC+UR~?&`wor*=Xpy624~rsTr9UY9;CFy;0dk4l2hUjF{s^J)Im{j zmzS)_nr&U4MrXMo0h&d-7 zpN?%Ox7-JfzA@SiSAV|syC}Xwn?47vigD6Hj&JJlP2kEwX8XdiP(S#K2`ST91i`~q zhf%MQwH}C68%Tq5b;TWWKzt>ntqbAA-P$Mq{t#^X$(p{@en5BbRxJ$t7IZ+NqURYb z;m1nl)2BI>7Q8RrvEw$E?gfXZ#Uv5~=e*@7sZM#7=5gvp!Rq>+_W^~Q#PFGG$l9@f zEr(s=;&vq?dN)NR5**OtG}a!{`0jJhh7E9qtQ=X@FQ(P$-PZ1JjF~idtn*=kUkoAf zjIOt9b9&zNZ|!_|{o42Tjvse^%5OKzlasGEm+cOdJK29ks1P)%T1O56pGUf2qTXXa zz!$seRiWCRMGjQmWN~_ZofVLQ{4&cxK1h)&7?WCPC{e!EFRW{4-SHIQeptb3pS5ZD z<+HM{rC|b!Rkqy_tDPKmoKEJ|*V=QDDqFlQ1p}U&9h)6sPgrp2l*U`|yCRcx2>$!`oThe6#R+tCY2XSQz{Trk*>3f+5o| za_WzZYAF5<=^@)I;)ov-&kEr zxac(E3&hXibl5PsBQfixvn;qk+qIkM*~g<|sxo{c_9TqLgl%Q*bg!nQe@T^`Cax~L z%`|es!B~%IJXa_RIhPp{d!Aqqh~^#OcB)Mt8k@E4n9rK_O5`-JmBGOK-6iI6KcT&V z5o7d8Yi0kNp$aw05+tL25%V4vH`DHxGP;z{6SUpZ6UNO4@(O#o+v{vvi}ZWXIftca zzkaN?!RyGYpS9QW*{&V`c(B-O9X+yhxBVplYapU=r(iJ7|a>wQlsQzhAcme#vE{mb@io+YC#M}DMU+=nQ@YE7D-p!nx=99|Zq zprWm>7amE5A6SLvZIn;YF_ELM1_BH*{@%>);-7=gXS;*f1r9My6U7oA7N}>#;_I)F z5Lux^hUy`HA@CLi(Ujb)O@8xsh1vl$X4)Q@YVtN_1Z1b(IECJ73p0wYaQ@>rW4ZmQ-S<3~Mx54;XGy<&JMaxDf+a|BiG&9?TCjr06`8`t?W%%P;yB7$iZ|c^~9~2WCI)3g1UaK8G68 zM{aw8BVuk#f^Sg2i=rLDan`m6Uk{@ncbxGS(icuAPw=cg8aoxWHx&w!{ruy*5HuXj{y@0tL7j`Z?R3JHs$1j zVOKa_DkU^Gyd(d*QK8@!ei?_6CbLY1@>-Q5K5vk%cF|{Pa$ib$H>{PXrTRT>^>+iRi0`4usZ#Y!)}8Ran233 z5g>$16qPo7Te39h?c5OHF6j@|$zL+kEq*;m!QnAOQnm4sfuG8}Pw`9SS|rlyj?1dI zsbcVb(yv;5p(?TEhL{VwGw@LroOKLd+Nj-z8l{a|Lo;ie*s5O)t|OZ=ahGH2r{&zG zx`&ug2`Mz>YvJei?Ymh5LSGESZAkcF?Rk1M(@$Xd)OOZ3B4<|#-Z=#vT>=!X#!1M` zA45?}zV{Pr$X*LWDOKs|t95prZQnI}>77A>*|LExMhRb3?b0?nB38q1S1!foMd-=rUsm~2wB`eln>d_g#^0P;CrL6UV8P%tGkFH7bbN#j}VXH8}(ts3Ds zuL{dx-UsJYp0(GZF7sTuF%*~k$IWurG&FI|sO9QuQmQa&RL^-D^2BX)y&hsZoKau3 zG}}!Hx)Ym9Rw^%lEhP7#P>GjjqM(JC1(d0O(XMRL&Xkj}^EgWXNz~OL>uxAm7XeVe zQp^QGn`ckJtr;IME96>!_~drBOn0w7n=g2BZlyW{<}~+s=l+k9o}`zqozW+~6!@&X z(>4%jIM!w!n;BozyYaNeW7lftd{cGjl=042SNs%8=uX!MvTcc5Ui#T`nNcvxq9qjp-RQQ6>) ziMcg56`)6G-$$=eIqsUp&X{>9MJj^8c(!cTv2a;D=~XYCt!HrL;l>r_C44$_#Nt>YxC3|PaW1lQ8l*6#o#%GPGln}yb=eewt>X?;<2lZE{b1$_MbY?C)iL#$0E zH4P@!VV9EwULbShiGdO>S=xV$Mo9w}NGlkM0M6M-&w_{6c}b!F*DL=yR?)X_#)@wF z+Odw#bQb2^QT=DK47fNU@^jEG80^sk4>Fs(`Y=5{)$1TU${X}<}3l~%@p1Fk@vaFq# ze&k;s(4+kYZ=L#wXWZHae#tBJ`~_3CkM%Q@9Zt}hYu=vNVJaphElwcIjEL(Sfors+ zN0x_oa{b=MAc>hPJl{vbG{6!VX6F~s1~QEHcv7W0LSvmiWP87@ns#o}v_EmvdP3v? z2v8OvWYLHIW3ZpGUj`ws6R#>iq9`Q#?#>i1rP{w}VB_z3^hJzkfCgY-1Qy^XyB?1p z4qRcQS^qgle^2#mtTsENCm;{9VEAyA;gQRE3(Q~{yCD%OQDJ54yGDAJSmI#yMYL6r zDze5Edahj%-hd|y)T;_oE{))UwPhp$73^P_?jAh2)dq9J!L#Zpq4zzR$+Un5IspWQ z+DR*yjPu#3s3bL|!Ra8G&tVrHe%;k|8rMs{H%c1e+*RfJWr~6b))61(BATyfhKD{f zlo}O`Ca-g!t`T-12sviW1B`d--+uac0Q>Yc*jlNzMWE$AoTZf$pe3Sr+%CG1I}EnH zk_07Vygmy?#C-Ke6!yC)my~bt`t<3xowvsQDPy>t#{C~twEsOV=&xRuPHgb{x$Qu> zWapXS2Lj+l|F(pb;4tq1B!PR?^C{Br#~pbqWc}OJ4~fSA|{XfyO~7F}$rE(RD>z4O&z z-YC6+UGd)4zMR`sOpT$^MH$8l{?qJn&Dwapz<KR~KEuF1KdwSj zA>il6OWuaB#|zWhJ;iT;ZHns$bw%>$+w|}6Ico60V>Yb2R>;%nl!o_xas^=|;%7xz zZ}2`XfJ+pbjOTRkxG?0-{Hki1cZ`$2h_EO!JxmB#t>- z_y2X-CfX3JIM04{v-@WZdH;XCqW%vf66J>Nkxw={C~eJDH$DC{{Enf_2nZPoOr1oj zsln^?x{PR@D$u<~|E&-CYXwle`fHZ}hk<{Q>i%oP_@7+@gv`(ub6b>s-hBIT(Yz#OQPV{ufEit0P5u^81OdR zsT#$rE1&+VIc(XmPa_8Yv;w}GRNQ%?0$p3C0A@Oh^i?K0xPhVobF!@E0g%Vq1eFF| zA6V(lXjm{^c4CMm0|07IP)X~v`;IQ;K^2a&&EUJ}v;-8`=VYnA)ZnFBU=Ap%HD(EU zMBT4Ko=p=qt@)||!rMoUb53Jsjp{xy_>#r#TFnDULh!^>!zg8?uK*(>4xo1Cq65@J z%_LMjhPf^~o_rNW9*4m9y;;nzm&3%#312n(8MW%hz!!sM%$8Nd++09G=yw+5+h{wu~SxJ?Wc{@}#4MC7Yd}R2VGv&!vI~N4tbv-uLHsjXRW3LaTsd zpBar}iow^PfBb)`)JCPv9@S_b%0=SN_l|ZY#ejFg^03I1zBM#lwU0zye{Q~A;V6qD*oN0fGz8l0Y$k0lhlc^nMMHFvBc;igYZpJGM$??& z(^b_DVEFr&9gqrV^}Lm8)eDoe#^Z=A6}{u2&dw$oi%L4he9|O&wDoQYxDMEu0Ms## zMXSch{e}E0pufV-Y$1iiwBFIC={`~Wr@9_~B3|0{%wy904`bQMg5lDFOBvc^jXxh- z)p)@PST!S?Xai5Ttq?1X+Hc0r^TwtQM{LrmPP5uP)uo7C&}3(B5A(Z2Q@_X?T$wbh zZOuI#Pj~Rvsjy8wS?xBcIXXAs_^+Y^3bmqj2QtQ>9^2sU_Kqyc2!>h&;?GxwcW(Vs9ln0kB_s zG+gB;eWHDc9xeuhb^v%T^_EtVjg=J|GOyp8xO1mKIviEAiknmCh397EmK1b=xl0&f z5Q0khji%)(>Qzm)v(+stz#6ZNyQLD3Bzac^$@^8Lch-)ef_As9iA}?wSvwgeKp?k= z4sbbRi6dlEn!ch!6ZkY{+Bc-|_;~C1Ihe=~vMIR%dI@qM4v$7xVB43u?X`Q9M6k}2 zIG(LDqdskP>yPeitAzPhjq#pyyPx+o&n|;(Tu#pwb_`Wx3Ti-vm06I=I0c?-67UD%D;GFI@(h^Ru zQox@XWQSP!s2@VA^%>s3;<(D$p&v&eQ@+x$rthsQEgCU5rtU(K za0t2A(GGVq_VHL{(Ej+Qql!%c8hB?LtT5q2(b;lw@PWg9hyb`27nU;iRXZI{IoYfB z8b;YW#HT|)-@j6~3du-(XV$7w-6m>NF;0_)FfNk8Q$@io)|Jd-K5*oD@yoh&4}bi8 zE_b*s4dJeR`sNm%*cwmccF~4-4=+fZ{CC?9lZ)Lcxma1Gd-rfNKbcDop~Y@v_76oN zYiFCPv__WctUTilO%m|l<_PB3gYu?j-|`0{i#fNs9+MDKXT*f8wh^K!U$C#D^=aO$X4PG~5-IFrbfw#Z#Ov{{u;(5lsq4VHOe#x}~pzrF^V8#WaM z=YtfzZE`rmM#8I6VcUMXhZih1B(Avimh61*#27Yrl5D}oNe>oTg|tcRRJN$@Oc9G@ zk3MeY(z~rE5c={Rdn1r+{AT^OWqW(9YT^}^pq+3a;CWZHQ&iUKoW=+e+$bOWUAQmu zgYYe)97n6#SEDdk)+O4B`jgR=bC zc5&svyAeC_$pTFx%Nt2Gc}iYM{wh=Q+4PnqZG(m^I{<3Yn7aFNOw@tgD+L9=UYIpL z8R33>J;teK(I=kXYAOaY&wVNM;IP-P12vv*s+%%nKp?0s`_@)0$G%j}a=CK8W`wR$ z2~7{t;YPjr0@joOpTzxcygjEfOB!-mhK3rtJs(2uZFDn#N^wu<=H+-M;RerD=zM6c z<8JP1L?k%pQHBnWV%&m|?;jc_NMgG=yi<^7gBA$~bF-W2Ho#l(Cv#gB+s?>Da7t3WH+G77M< zQ`vH*>*7^)9Wv@KOFhKx=}ERwoB_&NQvL4AotieTdAU-{HVEl>D7tX@aXvq>R#^KuBajUTkFzfhZ;yQ zngk2tCUf;)i1D3)Fq-;!f}}h3K9JakXb%~!eM4IrmK4ADQw3g%mNn#Hh%-&3}upgpO&)o}yGMfvnmp!i6;_pL>D=k8ru z7p5{Ww{jj-pnw?SIKNF4?}1?nGksS0tfSrp863eDD6_xo$x+oI!>fOV8|+f(bcod_ z7hBdclFU6#`2B!&sW2hSx@jM;_Yw>~Bkr*$5J=;)NWJBihlEnK>YCnP_b1vs2D%Lw zTn}=e7{IIs*7h{eY1%AyTzlvVxZF63s28l74+tI}T@G24fzSuK;gOZ(IZ%6D!-gv< zEbrQAER?i5&m=Ge2AZBm8kvE3*<^a4amv_sBGVX(#p!)y@pg4Asano*&$4oxyHr@; z=gf=5>ndiac%}Jb=Xs5b*m{YAQC%U~CORyVVOaAh9lIBTRY2TMv-`E=)>gS4p&g_1 zLxhV*=KQjj{W+YQaUtWlBz_xSJl82AaDc*z zXY93OLyLF1OWCdsaJ1$|9lcPv5CvgH2a5!>YAxFQYL7iLg$P6_d)7<;`WQT8N2K}X z;IU>|34xUD!yvLZ@x&Fm2q>?0a)>1nH{}g|Vt$zJ-)B7JCe~U%5)A0kF87Xa&JM&R zV|v&Y$@;GHvxM=`sykw^-=LwDgPk~0%aCk%YSB)c9y1-ZY9>B1XbO3bE`?yV{th3) zRrZ2cWrv>5hnP|A)Ag-oAFgP@_jSBlz(y@M(D?7_>cxGLgVNlw%QTgqQ&u8v$ z;NQ+@`ay=2P_(XlRR%cBBd_!spPwI9(zRo4L~s}kaYHX%;NWd!ga0b~$^2dR(?~?^ z9Cvlc7(Il>#e>RSlIOPA62x{EtQnG6qM3P|0q|TyDG-^3b?atnHnX8LsCosZ7S((6 zt{dIs6rZ3?%eB3}z^k!l1cf4^QV$Bgq1)X=V8-1H`?ay4;5sS502=vFKJsvwmQrQH zhv9sd1LtqoUQ9^7>k8>Hn5CU8NTDimS>|r6;eIiE(jf#=|D@Y=EIZKilKNqE>JzAL z7U0D#Ypp`0@JYjFRZ7|~Xozi$f}8K`^&FvJndEUbNo8zEK(!(-UC(>STEIQ zm&{Qb1<%DGUC;4*Kr)`}MEH=LgaMU>7hlIwMvsKT4UYqL7KPVL8gyc+rr6V%{6e4d zaJ<}|xm>X@i4qbflRC=|DMsLC)Uxo0LI~nS3_Ad5oN6N63?+32Q6Y2mD#EN@#jpJH zV)_7bTGbVKzsNpr_;{g<$fP5GY3}vdOb>8{Dj0ECM)>;bFN@XHHqzQvgKFtK##Pk` ze!MWiHdW7Iy<3(xSUGrI{Xy}^1&R<1IY5_nE^X}?dsPV6cDZX&7FpwEsRV%eX(EHi zXESa9bKyhUjrM_k+)5{_B=JrspwSK0Qw;sb>d1{<~@ zV1Rjr5CFKwqZa9ReF8`y6=`%_!ZKq-s8MmHU&y?tl@UyO-AU8JP}&4vJM#0v$OC#v zLEzLZu?j*#4&2?snMqbXD8m@y%sOrR;+3ZwXH#~jR2pDGf0A7EkPW>_D$w((P|L;o zPN&ydh=$xa#c23Ctj%Inu*!esYc~qXVuCxWp;IxF*(N;84q zAwQ|@*pHJu%4RI^JzmzAQtL)jUaJj`P|=o*7BVPzv7w4 z_Oy0TA5DM$Up!rfTT}1b7nDw=yGt7BPU%)UL|R&Mq_lK*N(j;*NDUB1gLHQ{ZosHf z@9}$GzxN;DV%s_Q^W2~OCKhj!k#Z?%pS8q8d2y;&?d9bjTFbFcRO~f_aql~rl8_B7 z=A7m9KKY~(mhr&^e|pddqRf1UbVS}$L=+|a;6_%lmkd-l zMr^`d0|EUMld7$7r|q5B)oRs;y&%Mooc$oH0n~LK@zWb3!qhG1!}b}44J$qvPNGlc z{P@G&{HaMUH6)I0k5_y|?2I?zPK-UM|80%2OFJZP1Wrm#atZT!-_3KMVIC;(Pp(Pq zp*O(fF)&Vnp}63hw_C6Aa-6#_R>30wcIa6q(vVeEIG)!s8&`n2|55PA07lN|&G z>`9^$Fd)*>s*a5+=OBznip;^YEitD;tb0&zZoJ(~giTS?)O3khAn<(#;%EL4e#>MFYr-CCG$w5lH=HV%(LEs`hS-%j?7q)-+3w%R_FcaC<(o^ zCxQi6;N4yzMmf>R^+%hF;|Pt#Y|PZPG0@7E*rrn8NULzu2w4w!(e zj<+4wNezU_^VsR=(Ks|QS9q?4-be5tNzpxjF??iWQE!=ld}y6I4%~~mEpm2==kSiY zF)ouYcG|G~s+}|=W%ZWCgi<`{Lzbxw=vgt?u)N7q+fmYXDF_u_`YE}jlK!UQ;Q~bxqdpT+oe(|ew5tP>E}Sc zjEO@X6e>fpmz@P0^8Q#J$&BXFEl`vixtIFuDa?lW&4jNd&e8ScK*3H4@_3S{ zvCF>5_|z3(r{ODug-KO)draUS+j~X{yua6kjG@;gTFL;bEcBk9opQjCggTx?eD8lpEY@E4vEcxKz+RQ0m^bFv zm}d1omQX!4z`QxvWy~7$>to)(frDOU$@_#0=-~We=g{iHbC~Gr_5C#zb#+4^hdfGm zapQ5u6cD=qxYD0c8DyJOP{o3!&%ERiC+hJ_bt1Av6LC?{_<#mB8KG;F9LHZnz4W}Q6Y<=Sr!BUn303U+TDco`tH_Ui&aB! zD<&YJsA#wHmjqEcd)bX@qaCH1OX@fx3QOuAR7LY9M11~^OLVg{Rj(Firu>?~Yj-6r zxwuW3bCVV-1TrfDUoX5k*N=?h1ob@EHIMo-U(m^S%>6dlWuc6JJEK5xYF&A~bZp%h z8I$K~A6qnkWf=z|bI)BJ2r?|Csm?b*G@9Owh0=|P4UKTvNb5bWK-(xxa`F zLp9W3zj7{zJHSAdW;h~b9@D1f*IJWPw+*02M|pz>GKjwIYoOH)lh${Z`3JSV%`!xq zIp!wC+Di6MnEBDG5PSAj`chtcj%ZBeIwTi$(B!aWtZXOjLh({zV@wQ+VO7moT^`S; zpM1sZd`quIg-9^747~2Rc7GO(D^qLSdAk>uyMM0@LYI#-Pt#{eH_1mI^!_U1sqk_i zN)+z#hYRgjTis&#DVL*<{WYcYwK@d$qJN`}fU`1UlZUozE1M$SpD)1X&mQe;xhH~1xzD`5{k^|gIu;_~2BR^m;}TD@THh~msf4m(Sg@)isoXq@JhYR5g+Hp0 zON;uB5tF#lt+wNO9CQz`;xFtWvFsmSgn5*^|0rKRv~3f&aEuS9I~HIqm&=q%NpPp)FN?SwC;|gjrjMr@(8^DsbQ$z&KSUW&rP{sF2`w)loRZ^LJ zh6pu`=Y(jnE-{G%n@iy~%3%goy-WOFL+r717s@*A>yGaFr79yLd6@D1F#s|uYkiDk zZ2?uKm}LlZ`U>a-(VW15tOk=P<(dPC_Wo=%uiLh}TZz-eyN6|cD3z?Z@ZP1N;4NBbiAy zfzj}h5Vc@0J;L|>H}A=;ZTc=o^8<2Y@qaz)u)5q5|IlR%dsXl<_$cAaK{_AgSvhZ$RuDa{Rg&gS`Z)Cx@!wmRrO!RBhSR_`UIm zc*)1qUeEQ4L*l)2)iKrFcnp<<-&hbc{(tC$_5Bgd;(M#aXG(qm!E|3aV`;sstT}Pe z5NoPzA1LU0M2wuY&Ik6BW1?F84+IQd4|nEjfxWsI+!!!Pci8v%VMHzOJYP&@C4peS`LfQ&sBxuFp#v7;u3oTa9l*j-ZQ8;I zAnKv^^Uip>m3o{c3h}M*yLSeN^17A>H2>(cGPhh>*8%Y0J48UA%=V<3`6aX?gO|4QsH-~81vw87_~`%W#R zrmWQuE(8}V031J31&mz++@PyNIvag~H3N&7_49|*_9Zym}P zT-BtM{_~>Tt<6|BHX}e*ZGj(jwi`QeM+UT0)7C{_7(4Gzc}&6K`81@Xe|$Ny;!@6RXyC6}54I`&s-zOoZrV%K^Rkb95(#cjyAsTpY` zp15mJ##jP(m)p*Ts@KY|m}ykL(RDAAPg5;P_7jD{i(r|6pR;;@=o19hLK0G0fVK1C z5BPTO#RZVZnPVP!+gH`Pmu<7^IWJII1#S=vM0|OE<*xm61AtcjEjyfYjM%F_T&=s% zWUT+&SR*7KVj+$H)=r7#<|g50&+(o|K)yeSH_(Si4lg&fPJ2H(l&8s0;ix54l}UTD zUl1gy{);0eWk*R5UX3H*_5Bvf}fG zeKx*O)VyJ#49XP;bf|A0tv8`JZ?m0&mOL8Y!S22Mb{B4=fjV?$>*TlU*<|optTCzi z+*uZ*{26OQ=<=R2JNs;<{rHt`?@us$Y&2LFgbJ8i43aj$na zBmd&1#g%sYQf6$&a(5p1PgmL|Be}Q{yq)Xy8W# z>GMF(!s?6X*X!HVn)&y^A{&Krf2iA=c{Op^auV(i;n0$cloIPtos?dqtVcx&*wkmk zF#tm}vg7=xtJ|{MVS54~O!xpe=%gs(tl)nL!L|S63YksQte*Qdsk>3r8f4Yn?Y49e z?!4LdbbBjsP=6OSTYHPr8cm$;%Gt;W{n>a4RELE2 z0o&pqtnLWJm>}HNI#R>YQr2_q1efd>LeH*$l;yj%kst6Z{jG1eU-r7D42ZLWAAQvZ zbZx~wCJh#lKR6z|Ku(N+3-~NOt&2EERL^#`MO*GUH674gwS@_CTBys2y(D)!VWyv6 z82-d*l}b*NGWULX1_U>I3*RDA629*=vK&Q9 z;Pe`l;p}ZElx2~Uya+dsQsQ`($xK@4#4#2@2?uQuHwD0^bYkpgpd?vf)xVdLRLbgZ;B_Y9Z6u)f&b zw+O9_3p#l<1^79!K;+D=sZtFBA3A=le|S^IetoOQ)?XW)d7*6lb99|o3KjaB{COwY zU>fO@S2JJ6L2oJL6S-~RVOck{MfCZMeemqEla|Z#pWC>cp;bJA0Zs!FgWu@QH|cnu zde1lb%w|}>CQ!sO5wLWeS}nSC-u72@E*&|q7`w*_n1~-TB(nl32I9$AhRPYcMlVH) zx&oE0Fb`IEq`u1CEnOzoH3@-(VmS*IcWTJ(=Z2PmBT{vkt_ktaz<{lGyoi zDXGy9@wY)<*q>~78v(|!Zrk~G0i(AY&5bs)re!J`NKs9%c^*8j@l)J)_Pu_Sd+n9X zMM+Q%!0X#&m=5ou`LK%q28c=D%lA zeR{cybwqn1Xc?8=J#@+uk)qnIL!QU5w$$;Nxo9n&EoJB z47L^GH5Ty+7CGzPe!h&-<&5yw$ud0-H}gGfr39hgepFj(49v=JhFWK2{}IG17dPsMkZ7tq3q3D@_i~>3lcgeQQyL zSzJ;0Q@*1I;W%?cG#Afkp=^Dn6NjO^2MzRlUG$`srF@p;(PFa+;uDIpd}4SY7WdrG z^O|{&{x@>@a^q>SbwJNp=;yiqBEl2~E6Cx$@Rmvt?ozOkH_3v2=)||x;J7W@lnw9d)gyS4_dA)>63W{nYQD z7A6fbl&U+V?j;3I9rHF!J8Yi8wmj;7R0TOJu|;m@Y=jq~{=o4l1^ z*7yNe_7|w0|A?xrisW8P9|M^XYomlsWkdpW@*u^2x~JkD)#_!6K8Dl@H*O?QN z9Pof!`%N6p;okzI&pQfGPm}oPg#Dx5zeDMY0Uf1*x8O8UIai;;#|q70YgTE_T$R8R z%yH8nf9fNvaaDTqGubWM8)Uf7Zn_EuADay^Lq!#+WSr9UMaEe=9yBJrK>98HXZ=Rh z4=k#tVtEsbO(>QqK4tiS6C}T<_%W}wPV~1>mod?pRiYvz&@FwQIN!q5P!a@SuB=u# ze5Rz}i%&gl6a>s_*pDMZZja#^FDwuuok(Ix}>hVW{lB^xYXlLIlPw`FSp~O_ywsJM9Ags_NNFp7%?Z+jcJrj)< z!E@3KN+kV3@xn(2*DKdy4kWE19?B9C%i$rkPVdAZM->ni-oV5A=v9`04B<=Zofb4| ze_=alp&~U-&#s71RKDf`NbJ1Z9n_47hN>P{mEgQW1J97#p|N-=wU|^-mNH`0On52` zIgmOu#!@gg2;FqLMThKAd0py$H=Z#uCS8 zaB6tvV<#hLjo|<{XIJ`uE_XEAf#A_?d*jRP#ei!mQvV=P$-ie3MwUWR)F$;cblXL_ zZK#0^7N7gTa;(*3(mwT4c+sNT|KRP`D{*nVI>OVMOoEN}>*eyG=ZflvcbeIfl_Q$& z=Gvk}Ka7EMdp)PWW1<6K`~B)7h|>0=*=up|CdD-jkkmJL-U;+N*C*@8Tn(%_b0U+L5YnAqXMSw zFP(3PpzlrhlHJhP>E}3iS2r-n8zyvb#QU7VBYvKMPc+**B!suKnC7?*aZM$bm#MFa zq4dUv-f60nYj-mnm8N85DX4H8-|@HiKbe@7aboiDNuPLx++(7s{Nd@;)^jR(IzznO zh_g+82Ni7xEgwbgcZ;GU907tA>ft@pgD0h=1j%FTr8q>3L0UHC{)t?inR+@}_#w)jeTESsLhWq^XH=5aJBl8prmyxo* zHDMDoUk?D}etay48bXjY$sOlNQlD<0un8l^t=%jcmU||)tZ}l^2fCE!q^7!0TpaOb zCurLmc8JYeXfIf*6!OZ3E`86#ik$z8LBNtdZ%(rv$Zd9GD3hQ9>zuKGKICSQ`Sepr zT{VS>eJbT@`8002+V1@(fV;y$O2hLh^m2oa`qjADwWIskVn?WdF9RJ|ch&8Vv!Cmz z)fPsKQ#&aOL&vRk@Io3v>Ao|fIwl*|c((#&9k)%EJf3|GiuT1Ga8^2VzFOFb%(H#L zUX=sP6J8fXXqjgf+IPM76RTf9+*wh6VzLi5o`Z2RRcIihpp@6fmT|Q6ydW5TE5PlHY_Kti16Uk0&-c5Vmem>d9 zjUntX?-@XX5|;;Salxf#?berJFb`mxc^>0u_mrytZ{u&YM#PehEe~LgOL4yU1lQQs z{#;AoV@DDt>QX4e>iv5MwVm%#X?lEcvqc`{ z_EB)I6X!K0s@p-kG~-IJ@8B3J?$dr^OQKg_w|wTqga&qF3(9MA$wP!smzzD;rZ*Rt zUf<^_3d14*br>B?EeC}r6)9w3G^L&6@&jhVsd_aFgAYFlz;;6fqvx|YZdL-uANGbT zhkeMc7%Ep>J-=LMAab1c#3kCE-J#{jG&VD1jDKz#e*Y6xTw12Ku{xF~A)%UHKOL?A zUDm(m+HhY~6At6R<4&b@K;Lr>)}N#@`o$w=%$=G@CQ$kh?!PX}_gyONEHr#0`8mIK zgXl0$XuToe^7;NEHMQnuxIpaGFyj_4C8)3VtiL<*h-$;`i?}IKg?|;#aJeaq}xKAF#&%z!Dh&8b!(1WPdcvVfm){_NpCf zvfd8bs7Av3dhBMjub&1D&*-}(#jR!Zs_zrPxPMBmVQIg9ginu)awqr{@-|RJsQ;`U z0e>Yy{M}?z*Jy4-RE(^$5r$CoV{fLw8oUR)(L@rcV2#|{QzcQ`)g-NmWXApITV z>Vo)#G>&*zxWdp_h^3odcV0#+{}R~4tet@2Qe_bPi;8XdO|V!vq9A!EDRgstFE^#t z#}pzxVTy&zd0^k9Z&oW>i4HnF0isR!vOZeq^?IJ2;F=G^ifBm2`U9Rx#Yiy84Pq{3 z+j#Dx7Iwng83Hx5k{P5GGsIKOacqd1rSpGzu&^O;F~owZ>h>3ra=G>s;P_Ax41?;4 z^BX@1SVlkiBZY&oz47?&m5XVHv~pFmfdo$QZS!JBz;A+Y1i`k(KKp*32X(vsE^jMm zw1c|P9aeow*v!~+@3YQ z5XTtYiN1~Df#hHy^08Bb2suAg#-i59od&csS;*--#u7{l(SWW^8*Hn z4ZmBnc!|!v>V>o-F+ejaw6S^3BKCbN>!2(kKjYnF67qNT0_Up}Ew3#?jirMixy2vk zH~AZMKIsx56l&kgYbN`jX6&EFGHf2XoCvscZgBGBd8y52-996ATI1h9dNH=$#Rt8i zlk7LnrX^pc1~2lItsm%s;~(r;R4|lFCJLqUUK3z_uW=&%%!8X0tw{sbOuGI%!)NwA zm(1xNITrD*=s;9onZY($0KZz2WVbGB)hp3ZILdhYy)K77?>cBm&YZV@15Qq7KH&LV zCKBZwb+0IM{r>XW+@>X9-zS51{drURA}=*A^?gf@d$A_n17NYw8zZ$(5ZPmOKmN@p z8N6=Dh9No8WU#HHzth57oICG#pGXkk8gy;i+^YM+?Fk@vGh?_89mXW~IqtgfLSl1| z!u??Lf^!#_%zA;j_MVA}(=e$~ic{b(t1VxtcBEQ@dolQLY$Oo}mKTpZLmKHX11Qf) zRK%FSIu8e#-*^EUOj%e5F6dm_8xfzHvHLoc4lkEA0)&fhaQb2Q7m2y=V$&PP|85_l z<8oK4Mv6o=&K8;5!5buk1DYd2fUu|voQ3w^q?h0fpHb#_$Xam0bVgaz6U}j4$s1hK#Nh$2LmlbIG#>2A#f* zY_CzZ>G{%$#v3IB$9yQBNKal769|0SPdYuAPpPHy$`BQVpoRo5|4aGZGf&KMRVkUP zr&kB!g-0VKmpTK)%A_I!h7@T6w&vpeC3C&hG0H-LIIErocK2afe=whCx*?*#H&-XrD`V$}q91=LY#a|iP zI%TN`Q%2X5Y>kHb%9MexAlBT-CH3ui8gaoi5oEPa*;@z{?aD za#r)Xrq9qhG272Vgy**L#dC*T0>Yay#O!hY$RTeY8cYEsx*$e;Kw6_7esmt{%V{`` z!xy3%BC)hhlulCRr*z+8qBXp&&Jm#H!5q)0Yr;%_Be)E|Tdrm1&ub^scPCXDbrn0j zCQIKs%vB^dF@CSJMcV(z(kjc-#C1~grTVpM2v2D z({WSnE(SHsya&gAaKx7_NOASSc^*~95c{&^DDoK?q};{XltJSo-Hq#f%f0*IK9wgR zj(qydk*-_;#t}yC3+=G`5=xjsER!U6PxFrf!B{F;ebZHLrKgh1uo`8*3h4lTdu}2a zGuhj6A#qf6>prx@(&~-sn`&jhUx_e|8@q%}9WF4pwB8H*e0{TfX}3&iVFj?1QL zDHc|F5Roj154GD89XyTcLRhtf>Ef6@6(3}H)BeazYpDq03ftH95j$h6R-@mJ{P3-I z1M9#tF@e8ZEUPyZ?L`5)})Q6)fDixJe8;_P-@(?a=KjK%faz-oC zPoW$m`GuGDq$`j_sY)W^`Xu1jHX6*40Zawz(Jhj?hWKTiC;MX0n~^o4Z4~kfY}gDY zehU_lgeuncv_tOBo}Mn}&1EVBH0P8YaPXeHq`_3!)}0m>*sX!f=6VupSiNjd)3r%z zZHK_!=aehl{h*6P3?@wSyWa%yEH?*4i?B zXSiS8I!HJDQm^Fj$}q~DzSxequivr3S$DR|f;~zTg7~3)b)t z`Hk70_R0Mw@M|YogG{p*+a0OfK@7>nqC-5uF)(3gj2xX=v4?*H5ze%bG&qAjAGq{s5{=m6(L}?~Pr&-f7-g zpmK4gTF7A33@e(un?FS7F_YKwB6>UoZ?802XvaN^{YW+th^JtB{K02GpX$-+d;i)- zvv}9qJctgI9y!JbL}ZFGb$m}-{27a3zYn!DR=6$zA~0+0LDU{5q-AiR7kTVpObwQg#cWKfzf@z?_U7t)d$h>F^E3QaR_cBCTEft;bTGnI=B1PdBL&x1G!fNEkyj zv0jLUPx|BYa0+6T9zrh*r{0eltj{jndnLleJ9tRasV&?(YwH@diey3i+@BF{yk}`` z7F$g3(r_SWIGdRYKblR>RuB4K+pDr69NE^aDIu1%&0HXP2&x%TpiW7q@c$}vZ0?$jHBlWp{Um_9|C z?rR?%hvPSsYU$KKlx;P2BV2`m`ou67A{C$20Vav_7Z%&aVfN3}M>)Sp_4{!?bt~25 zAM#45JkA|&DglNubtWy|Dl;32MJI1jNo9@x=A|1pq)A+Kmb&&d zRTk)`w$-7vqq#$I8xZ%XRI57y)Wl9D)&bk zzPu`eydrs=#gJI{ErBV|hbEA1k?w2QSQwj?ib zAm$)XD-E3=zz@X5hq5M@EaGP555F7yQHY@PBzj~?00tzG+sk@4<`?44Y9X-s+8r6L z=>|r%bU3U%AUW?C@{aoRUe+G?6y8OiK1*b&Gi*oyuDi?g+6j#}@=bK=!fPy6*a!B* zp>SVZ_;F&|nYoW(O#|H^w-l#j+f!jeM0N9x^`=li>EW_uQ1^wbgNhGVYA+9%&*R^v zwvqQxpn5dlT5aN7J^9w}UuDS6hD6CO=p5IxD}ez`8FV9AtkhY|^N>~YGQcfY07*Gz zFWXB4XcbD4XP)(C*vOC{PeaYfPX0FxPx9vB7fRe7CY{4U&A1D~UUcv2`1hl4h{XCi(3l z`Lf6}J(z&wVJgVuE_qGPPO%_z0NpS#>6+q7lIf{sJ)>9CH+)MCd$jO?P=WdU$8f}^ z5gjs}UTWiS!u4@@5H>GLg3do>=PS=BOVS#Nkizlb!qVW9)Q)04%RvB=!}Vbj&yUas z0SAYtLIKGd5;t+-WO2sp9D&+ob?Mz!*01Nvr83Xfgz9L^N!Tsw+FUn#rUxCmf zLx|TrL<6|(f!1W}U{;hg!R)lp@@~nfG`!EGCB6+}P*Bi~D2ereE0rWFmIXeYDK*QD zM1&-n-OHMP(XQKIVEF}TL8s2WXAH3+rCf;*59%Sl46H}!ubb3D#4UWHKoK)2^CS@+ zUloz;Z%qeM{2V)zU361pmUv9IR(+SLkj$m`NFfUSJ$LN#h@6<@E+z@rWEULk(DkFo z;PppJMJYvce@iz+Lh6s&#L~$}u~X#YF?|Zrqnp_?GZ-g-WRlmA364S7nWqz=SfeNz z=tn=4YM|3N?>p2z$9^zC|6CphSzxJB`@?=F9t(S9qPazdY`t&hak47Hk$CVLv>^vS zr{gTxS`K$+IO!6!kvDQ8#eG2a$$u35@}>vwCog)R?wvTpV{+lwXqYl5Mf;RZE9W4| z86nvA=dt7QFYTM~AyvKt!iUr`xizesCHz-zFk?jcUXu}5$bjh6^2~$$aTnYNU2d1> zmE?1YOy6LgF=PIQ!IyFiz;pZ~BU{*i({^ zz4#bot$^cVK_IP&xayOPi2XVSqHYFH-|_y+^TnXKZaVK9 zC-d>>gqooDo!9G;fkG{K%PkRCK#_nI{Z7I)K}MrVs)xFAMp7>i<>6~1+pJ&XM}o{9 zn(RMq$q0a^6L!FF(uW4^e(K+Xi9b9xQoUCf!cGul8S4r9;*@=+dK{O01nBHc=KcaH zJL79v5rDbT{PVlAnxrRtLbE1=GPC=TSIheNhfBj>o+}`uE0V}Qi=fx0$zZ@j_Sxfh z?v~b>+61NYmT>9+z5Ga@n)HZV(L1f5s(sR>r(ze5!<_9awE7%RlG3U=4Qk|uy4%GX ztX<??Nhxqu z7@6s~&XwU0v>I*bs}y36k21=oPb1%-k+L4Ru8eq>u&4oIbXQDE`;15C%X)2#m{sz! zkN1YnY(oL*Z6Vx+raX?_HD0%I*X7DSA7?_6;z#a=6{Xn)OV){XKc`pB>6lSMY`aEF z0Oc|v(JP4SdY#fyD3tU?UtU;WtIFeNDhc1iJq%?$Gc-xuN0V4abxMEQASM3}B#n%3 zpxCe&UTbpyMvInoEG8Ed=sPTO?Xuy6o7r76;`JVIpb~{uDE=DOn%_cCdHN6ew}5UJ z5U~mOn;`IN8kAl;brbPrkv0x759y3Bwu!HL*Rk$TzdJr8L*@Or@hr9;_HP*{d5=B7 zlOOV7r*OF(XV@ex=+O?szernAFcJU|!^A||l7H*9!ob#GdEtIFLk~qVAlh+;`zRVk zxEp)72UgFV2y6Hlv6*>SRC~~=b+ESIwd6yDwI+KbSu%e6@SaB5yqp6>Y%Skj1=Lz} z&BnO-6oll!m;hyN&C&)*&PwXE3U={6k1a<<5udqR9e5BRRo4#NX&ly;HeTn1cQUt)by1J8p3-T@16BCUz5?}@cb z3^W8-T^GUXDe{UBlprubbvIj#NkEfDkqb z2y^@Gt0^r1WT6^iq`E|B5^Fn%W}e=v&^>;iJAq}zmA4&5|p9-rQp)Y*pot9}Io*jp6e6#rM^1n$1PN)c+^>wLo z`%qkUfDUt}RSiGe=eRj=`{Qk-gI-(ZppxvZ197d^pX4(J9db-x8QPmK9Un{%S$Udb)ZJU(s?&}0A|n)GFWC+J09wWw~Ug)mM}9o`;{BNOJ-t~gTzLE zPN;HcKI~46yYDwy%!%Y7ik8pdS5+<1p(2S?VQo~!z(B3e!nN>6OIqR(w+gj`y3AM< zynC+Y)AxJ3ByJWfY-ex{q0?1;idaCafviMsLc(!F*^SiwtXxat%n_jHWbVszgd^At z$|pUe_>d<9YW;*Dj9w$G!_$MW*db{#FYG%v^yLe$=y3*8uxmtsT_L+V`o%N6U zo1lN9kcqda^|MksMt;m$r=ai(Ae6eaM#~q8K<%b!3I%vsxRV_e3dHxDj_AHf26tj~ zGQZV7STe%^b zOt&Iud#+DfF1`6m;gt}sq%SyjNl71gq;Vvdc*HAkmb6$#c|>2EzQ@bxYnI)vBpuz5 zkmdSHuU!<4T%lEf>f-&0J#y|iDY?)XC1 z#HP5zM;7Ygdx+MFR1?3wKC(qUMSRC~Da^Gk*#Da~S)?XiAUJg9jmj_MbR5X;19Gt} zAH!Z|2!qcvCD>aZLZUa=3E6jcawag}M8S_3?#OhWiN1&j9iK2sB$+9~wj&hDtej$O zc;81+UD6+f(^8+P@*bH>soZK7TJ%=|d#MvAvo1PbFp@W2@^$rX@CQRXOPNfIo9A=E zQ6+I()ke%o1cVL4QcFB5LI}>8tD_r^p?lM9Olks!1zJw%pMA9C@ATM^aJ&%P=}(7)GUds7c=z<@Bt)gR=~BA5kgHiYT|_v-hc& z-I)Ohoss_(IZ}Ga6QCKNuu#s~-ZG}z0vavBLs<4%Mm?^VIR6=bg-*q~uQ8uXFXFR8 zQT5^02lxEeWN(5qe)TKZf5&}xERZCKFU^JJO9n7tZ}BiE`1xoWrDYap)!0itdsUId(3O( zUAcYW2d9A3!R}d;CBJ(T1+KIJ+CI~%b$Z<*4SE`q5eBTMBydXnKMF;DoN1S{QlC8H zNy={?$yTAdqnNf_voC}-8b@9xT1l4;Gn(+FyjH!X910fk2@VlEt6-hbyWhxP`40{3 z=xJ3*6!4d)ZJ(stp#|K>WaDFbt=_|{re3!_33;tqjkn3l8XBFsiKH4IeR^xV%%9pZ z4Y66+#OYo8NY$_s@y+-_^i`yC1p#XWzSgilT*FhiG}N4QT9GM)kZ3lThYr(At>*lj z(r|h+y61U(3xenbaUfd#B790=3x%`S?iF$TCk{-K@J!_J!c+qNE@YMA(qj%snJZmY z_Egsp-UdU%iY?vqvH_@Px zz?!q?pzrUNqm=z{MFL~m^^Dl~SxuXS%%X;V8~&G4l3QwbPS^eIKP6Na!D2P+xF1`X z-Ab86zC4Q$CsW`0eTT*=jX~@Nn`9)Td#xhc5+YoDM+;((PAQ^~7(o{5FXLI18`jCL z39M9#dmi?qA`dE6n-uIev_&CGO1GKATPsh1u%sdI&G+V+O4*!#@ zofk+GFdP$msKl)n967C8vev-){pd`i0C$VKn9?$i{~^pGY&J);37p>eaC3o&05Z`( zFKKJFOzp4)7;?QLK`8!McU*V**H{i`Y5@8%QA2;Wr zFO1__rDC6il(JuOM+d5HjpT|sYU6LW3{GdGS7=^i8r8i;Eb!A>`+|1th8o_PhrcM= zN62uO-02PSTWDC>uj2M4Y%OOCl*C0g9v0KgfF)Z9vSp6LQ6x{uIvf`5W ztIPCLfbEAJe9b&&1fcK8?+ajv_!d zQ!Wo(uL0xTo&7etD2`LTX@cC6aMb$E*WQ7s7s+M#km+G5W#aBq@zP6j>&F_;n2C&V zk=3W;)+k)=1}l^2SUU6C9y1JeL~DYQAPOa>#RBOFz8i z6PbdI|4eQyXmrve;yia!K6sZrOo%9zCQR}A0`Ebf_D*^Rdv@`Z-I3Sd<2-ci5D03H z3Gs@vNmf>Ov$3(qe%Jt7B7_a@(`~(RT9#KKzCM`{|L5y<<}C3pyLU8!=rMcu7Ic)T zMDqI#w?`rt*}OjnH; znO@gaf^+~M(Gto`aL7QVk&sG&KnLfulg)(UWw8hRjwRbzJ=5)%s%i=vueE_WmIO@M zm6D6=WM?T>zjyxlbI>9<@@yX+%HvjUUBuO;`-kKGyj&wt%KJ4 zTPMdZWo#DS++NsM^M_<>_D8hO!Wf$`k=TPj6fu68k|lWclSlz8kKQ2Y3beW1e_4oX z9WBiIHj!kD&IQ{*JMWit6g*}rSeh0Z>42Z5%}p-*5*002W$PB>LdS;n3u7z?^59Q@ z)8bdU@Fci};{4D@_F>8r9+|9|P}2PG4?9@;L84NRH!1oVkHrNk{^pPHi!H*M*5dJTv^6G|zINIA!KOfI+Dpi9F*m8n+^&>-jv zZ4FxHYJ`_Hvl~Lf7@=R-iPat$F{|N;D9jHv;C0x`FH9moY)?42RakBZnPY@OL}0`fs^??fhJ^Ed?{CJ-kH3P7%j5Ci2PU`48)_-aXjCoRolFCBRJ^f(8Sl5R zkD=>S#x8xtwrM!!SJ9|9ibT0Qnn_+SK9-w(8CclyHb_l|S-8Iq$}L={VCSW5_`Wvt zZNI);s#{NR{)NT$j_cWCh4H8;5zZUMce(+?5kY~`ZLQ}=$6(PYIc&u)_Vwx-m(^OdOO%5$M(W|C zd8XVT8Jb#gx4qFv6iE*7(N#qFY~gjrRn|DWl`%M{pQ+$5!^%k88ngNsli}m1yw-e8 z6|uR1Fz}nD*1^}m4G18YY*I2WOaop~ZDI0K+N)+O@6f0-bK**Le5n6s^7GHS!rZ{U zq}tp(V^`f7GbgWjv<8Y3>M8P5DX=pGmw_F!x$uo1!Bv%q57YT2F_m3$j7~ssP*p1D zSQDeiYh`Op5Vv|J(pC9@qsBN(kKlLO&R9-6tSyxg(tJW1b+zQHSR74P9wnyJ_xUJ) zHg`HMi2^DP$P**xiVs~M{bK*nPOEkva5tuW?xi9~j5zo%jU72np`BRp(Z*@q8@`oy z?{n+!uj;Ehb#LAK>-?lDp;s@SIiESloMVil?ov6+dAVVSPwRlMP3~7TM50?-<{%pR zYSFe2hWc}a^6$&S@&%Gqosw$SAKvrq89hcG8J9_iz6G<9Ae=G|t2ZAZDj7U{mfBpe zeD2}8Q;_b6HI`JE$F=KQ%i~T;-CMiI3_AhQV#VR{j6~SZyO&|4C1bpnnWJI48RqjQ zm;A<3Q#amnjWnQ?`QwxD8A$Dgr*KvZqyWF0tLfMXpewWFLf06*Mw?m6dye@LU>YKX z$l`T9cQ{fAw^*u@zj52&eBnUYE!f8e!R|Pdv7Ytl`k6bCvG4ktZUJ;0Bvh76r$tav zjv7vtOqtTio)_TrnNo@EeS#o+u@!UwkRftjvYCfJflsvq{=1^tp>dmj#r^yB73(cq zf#}c{C4<2wc-|^efmS^Ihb~fd9-Y+Jc6SL*L&VWjAG_XSC~us{eQy7l4!b)|MQ41f z5gYJXtW@l#`_7-4&QRJG^w35tT9Bhhe)dD8X82LZ&w8m?F_Ug3v_gxZgjlbIs>rf7 zl(!b-lJ25=+r8%g+(@X)&{6*@s-G!+&b|t|OU}zlzr++(uAU8DM93TbXWt(Cif}%t zwM>Nv=#^p6lWm{HnmEzSy&@UL@)UeR^ptt9}wp&7-%e^s0ifpx+cT(4BPZ-zt(^yH6cIrVP|G}d3 zyP5^+IlQj@(VxrHvOwVWJpio$my*mu!@H~y}S;_4i6Rv8tK(}Z|t`X_vTo*6NOlMb6@KX;*%LQ-|f!mB} z5IqkR7y5Ej6aPY@jtdtkRh}C)JsbrMq<_}?jr4UvQi**$KYJhyGluk`T4LQwQ(e0) zW!niS8od2W^>%H&jx5K`j~-6qHp57S?}|_YzxMM)swp;D*S|k2DfHi_4-1ZG+uc(X zk$r2T``dd$sB+`BzoY*UlQogEkAOV-n~k(0cE3-(wAZ^+ zD?=!TqST!gui}9St*#7toqz;h*pzo_WE0PW?-`g@u&DOcB`Ji!CMtuhgKqe_kdofs z8;js44l^5&S-CyoGyCULDXR$`)X-wZJ99nsZt4ZYIHs+HpDAZk1E3bjZ)cKh|4%E0 zRI3yt-~1I+!ivt^+mS>DB+y|*^db`5xbvWamoLNmC&_md2Q3)9VuBPH5E*qe}mm)afg}xkod#;Y3srT{i9F^ zFs~)4xxz$d=-+Hr=l9m0uKf#6t9??Ebl6Buv@sHyv|i{qVaz>L zsvIKed+q9dOSQ3sJyl23skFc4_(9R}O-!)hveQq7KeyXLH!Q&;nEa8q!(5MtgPg*6 zH)=UvUZvaFbqL0CqLCQ{Z-zRVcQDWATT%q#8#&)boht`TfsWkCB>XFk+Nr0`l27OH ziIn-LCZIST5YnPPvCmTo2J1m5_FLvxKJ9yLRIu*4B;j`xK$=`q92d%ftq(Z)w)ppv z95V%3VgG(_V)r|_DCH?}knviej)8Hszd8wbLRY%JEiSSkmvbtWNLWHPY1WMa@zu{;#eGh0okb!9kn= zG9E%p;QJdhj;S|~_9#7n+;i889wo3^56;=`n+;xqXLbuO7I355F2iizYoPY9tE8qxjGQ^>(MdH zE&lHrKVk2PyXw4K=UcZfAdH~+sph+yz`-roCxlY)qR;#ASj=FJG#beXm+A@WRi&@|oMt>boibO!y)roYZXw~p;_O>;pfJ)*M zXz$_f?lEK~1W>+=HGhy^DLPGBAw&CC%t^GOtzCa&Pp`P(cdYx17rp8??r8^m-x^hZ z8ar0WaI8>%KOr42@}E6%th{?#r;NzS___m5g}IBvn43@M7>VLR-2LI$0bY|m z#0F&-XtJb;+Ep-|O~E~YVIy=Y#Di8`Ry2zQjeXMJXr8E*2sz)Op=(cnMjg4+^ zt=Uahekh7Rfu@f$ArqA^gcZxkgAx(J>7LA3)i}>qNKxa%;AO!!L&h zP0%Zwn&nBri+B=mY}bCM#3goKDNEtoD%5Te*3#D$(?RZpjv@7#cg>oE2}&{ouL1zX`MdI zeup=7#Rfg7s!qO+L2=-i!jhcZA%4z}HYwm7$*8eT#Imwfx!k_ny)f5M3HviK`)VXa zhk*1}+tG3}wn$CDF~{ZohDSg-x(RW^iwx-E`2hSpXNpG10z>?Np`8 zRFBzL`F(enl_szXGe+ffAH0-vH%o|ByanEQ9mVr}gT7Do`?Ka-ptHTp3Msow1cW%Ln7Z?)-rM(!UO~ z?&;E7|E;-QD=L|+*Dqr;nZk_QzTVVPH@bK<_y6HUMCiDRBs&0aKsifbp9aDRZ<;IO z_0Hg-+r6iPt1TTevx>{ziR$`a!;0FatH~-wKcHA6GckB;f9I#upxQMBt)&Y`AMWGKR@8gxT}X9hH{1 zs1Voce)|x{DU7Vf%?r7|&HPI@8A;XmwbgnJ67n($V?^e`uNuNN4h@v$`Jq;BMZ%G` zA6ieZj~lnMpZ7s^E4kR7bZvAj+tJi;1-IKoVy?Rz?&t zTgISDvDdDyJ1_zXI`j5~IG(MA)1<>Vw7va)6?oPLkNQ8})mEn{h(1yS`j*kgMR+9TLezQS!Qeim8 z>5(25i|YjbD@NPgG#+Xw8B6@|S0aw;RtNOQJU0UtoBq^;8K`c=mKLA#@a?Rii^5xv z+D5b7_6Aq%eVxZVw<+ZHob@jDXI|G^&G8ZoERL}2Tvuo|_%xHc^wr4~H~2DfjsiT2 z)hOXAEVA*R&fA3}g?MB9v=)zMPpy0 zr|(y`E@VOP+VW^~a*4BIiNo5VzaDGH#rd9*u%F()@8Iv^SKsiJtIuUL?Un!2>*;*T zDz-!x0KJ>3u(g_5=)Xvw3a_)Dn-#qsFi^>tdWxFQW{TnR1}0>TSx{`5)CZg4VTi6f zcRN{hIhq=!@TWEY@>6ZJOs{?6CflRy#B4$g1$o#Y*%wwIo@yrA?H>N1taHa8Lecuf znA&d#X?kmCY|Q)qIucfvv;D@??7AwS@b^L6F!j+2bbPUw#hAmDSDLWN!NM@>p|66xZ^YktlLytQqo+u%9hzbqXLq0xM8 zVNa?2YCg-mdGB0Ao8LtqN@j-d)pGx;_at1S(94z2as8)RW_jb~3Xi^ENtRn!y0B|Y z>Qb`$`Ri02N^$*T+C#Rr=@DcR2t8*1F z`M_(*qjNs5j5#L7OKJ{1e&~V8g^?kyum7T3sHt-ui2RIkrRHVy$mOCv7pVOwWc-Z! zPbNhLWcK{XHSTg~*!OQ?fOXmpI7j$e%?uNdwB4^eOczMkO0AV4FC2Wft(5XJ1f_8K z&e=3ByB(cu>-!kuT6h>}d>jX{DErd{`L&x4!}>Up`JKQ^cmmAoDV%K>;Z#Jjv?s|0#eyJ(k{(LI^Vip@QkhE4XGVk)PE?=BfUrpBR z?X5#gT%Fz)phw9cuGK67nzLLHw8%S@?{H|rxUST{N}y66YNkyibvyVijed3X39byf zDOFbvrfC}=w9^X^UFw+xZ_vwj62mh#M(!I4k<- zcjwUT-P%yyFp9`zi~AN|o*vOfYt__OQ*>n=aNCb%9;Gj18v!B#3(rVhg8j@xl3D8% z0vsm7sXZKq^H}W4XvFqjIvYbWbs+(cG^a^!7T#(4n^T!eIs7Z$>biP|V3)~8mz_WJ z6y0{M7iP{daxs?_&vwYstsm@!IfqvgYAnvTFky;SHwp-ZH-GdG5MXnnsrsb>GWzqe z_OM~mYeJ&ZNusb=ul17p-WO%QSF6k2=>%}cFgt2GB;kr!&a$@ac&P!w_C;VaQ<@)y zeIb2V4zy_++R6L^aTrFqUc!BsK##2Puxyermo%hcIm znn|x^cD=58^YazO24A10qsGQBAC>{hFu#=s%}E)k&omA!E63bZ5&$`iY}I(di+)ks zC5c~(pK#vIR&Z6OpZ&Nn#1J(ZV1U6M8a+?AD<1%Ri}o9CYKffP6@(C1Ov*gX3n$2% zVtLqgf!K4UMs=X|U*EKUTy#ip`j&uMzQAjpq^+U;%fca_R~?=ZOQ zp!c(7u?K9LlY;8Zr|r-tNeU<+r>~pKekxmQhMrC;gMDTq8uC*ck}rt6A!yPn2BgkR zdk6Mxeob$WPZi1d>A>$wjSArvgS5A3iS3sQZcXM^tNZcO$TA9?-;E)9S{HTDO78Y=xkO6HOYbtdK7n?K zCZ!C1s!0O1?Vzms(cs-lDLduUTX99>^6}_RZOFK5@|!?C=0>!a3JA1V-7m{2?c_HFCQFdqeh=gbEVVAO;MhN8cG#la_CV+~m}##dsOr8lA56QOMqW~WxAoQ} z2bHFBg4Le8f5tkmay)I_C4n`GcEkR}7e~xO`lV+!V=q2%+b+1VFp9`|)Hyn^Ruv;}=jzJ}>1{_VgFR`$k>S)|Y2{6Zj8z06=JnN59YB`+=NnbEaYtIA0@O(D-9dBqcE zd5lNswwpVG*ZKsz6@Mo};uBpS$Nqp_dDYK`9EPoGWMw+}55k;odT3PNO+t0;&C_sc zTq@lgjpm#`rg1~^7~n-qj+K81N;nL5L=hxUT^Fico>IDa_<{^p#q#^3_FW2AtcOvB zbMw44N>4;IsHa49Tty#GnjV_2V{cQ0sv?p`=Oe+SYU?4o^{*3)4*T^z+Cwa!R#riIvw=P3|JK)mZu1(;}tc zBI26DrFG;@lJeS8cu~1mU|EYV*T zPZOwa$+|bm%Z56?b;@XCr@CxK0a7pXu~mL7XSX6_8ejR1ZIRSgxE5#pO_=tcs&NIc z|8i1|r{pk=6?u?rD?*5@-*NDLXr^Q6e}iC3RX~R~+b5m{P%Jj*#7iQ9_ru;@A7Wn0UF-AM zL%xBN@9#+#PE!&-$lme}s#Gv95|wsQ-o6t}t-6i6G#8}xnAHxRnjo>_YPqh-qb2tf zs%EtqdAm||igr-<@>TYeJ%!Xy&~7K=Pfb1}%1+78QVuU#2gGhEHDrUI(q~EFZ*up^ zUh4o)C^v5IDjGAtLt!u|tG#itjl(v+@S#u%WeYcv!Q}-Ra}fKCoUkkd6j#G=?4{>w z#v#U)&lcX#e90R8p#q*9fx}#tiMmj2-8etN<@F)3@Ttfk!(NtRzf5jrxuA8-N75ZW zgcoY7VDiT{Mn0FQI<+y>Qf`|rg(&+~>4OgkwD%9s(dIHZfgxiI#9M2gXr$$Lg zW`AypA?ETt(!ip(e~1DdC(OHR4B~_9ZL4({Z}J}AaUnNTK4`(uD{59)9XGMapYkR= zp5tT-vM1e{lAmwI^Q~@*$fVJRKTx+k^Y@k@Uqv5|Z8_XMT?p-sVKjWIR$_{yYZ5Y; ziVllnF`u|fs}zk_vW#ct7T=#{EW%lf@?%o~5%Q0BH-&qJBsGNc*S&4++>I5c`7KiB zX=sPpULhK+$_0_9f3$ek?Fd78XiIwFjC-TMa3i8LA%(!abdp~F6=cfET=Jq5*+Yl) zcS<~HAzr_Pe)i#n@(lZsy}6$avdmpZ)9k%NyxG1P<%-@)Fe^s-bB%1e4v3<}I?ZhV ztV@+8VdAfR0Qr0C>Gx=xxjdDngZGEOT)EHp$?Wfyh7SyRqn346>l3O@gpcm&F~wO# z*cq{y$SM&^9qJ?5^}(t#b=Cv;gi!xp_szLGTAZx)yv0lqwMl$c^_v}$S>vFA6E7-a z&mlRYi8nV?Ca=WA4dbHU1VVeui8T6>-Ooyhx7-0r2B(jS9js9{+VSI_)=*4`)h(3y z{WCwm=>1b_$A9|k(;i}1Ql__sBVKktH@-EeHm9tjHq+-St)P2ZJI?04Gted!8|Ay~ zae_nx-)$DN<5`*SMk|5Bz54Io(=E1pX%|RRTzd0@SDFt?Xhbx)N8XzQH+s#B>h*{g z*YWg4h(NI}z?*x-nr@UDCw24E1k|#0_3HEy;!b!sys2!Q*`^f&6as!*ul)a<@$-5w z@X#II>q6c-*5?*o7ag7*SENOMpcr){tm`vf>yjqtC51^>dEqhd&MWR7xliOWS|dyD zZl21ElteB*GEzYTzOTX{M%b9s&bunMllGD%lh$PMB_}w${Lxa>gQ|Ga7g;3Tg(q#6 zJfd_+kIGuu$`O=>$SS8<0(wT7PQt`zO&w+*Y8al%Z34^uH`oSa;c75gZx@y=)jR)rS4BPIyQPk zczPd5?oLic?O>-qItYfc2tRDpx3%6q4v-jTuw03Bj3z}AN#Lu$T!fmwm|HhmNL&Eu zAO46;F-7xK-5*-==XV*3g3J82HJ-8dk#GAF+eb=reUI3Yj;u`S&W$tYr(Xi8uR?j& z-^f7h2K0E-r@0+E)3EIJljJ+}tINh}aEHNeqG|C|LvofWg9Z~PRtZN~rOrc6^|R2w znz-B|@H&+QE!r7)q6z!dN%R@DS_CP(jztQG_S%9X)uR1uS+o1t^0s3?$g>0hT>Cx_ z)ITHPtf`q0C?4~>oA>KbOZdH?0R{=kujpG%*d=h4y`naFR3CDBLCzf*+6SJicuiCB zFmZ2D9xz+3(y!SW-dkCU>)Bm^!;UBzmwYkv!&K{gY=&Ni!w#sdYe|b#%ba`rk26?o zWW{^V!E-x`q7m`ybn4)Za~W|VS9ZOFMomS>oZit_ylRVFwsHP(!%!UN=Ve+Kfs-Wp zQ$?Cz91o2u>m#cBG#}=Bb1Z_B*w*U`o3^BuQAh{69mvky8qeNqyq(9_CA@KZ>jN%e zAzHvSA@y0Dh~~HZ&7wvV1i#IG`Yh(9U&{j?$-s}fKf|l2-#8kVc;V6wAAF5T&D)6D zNjmuYcU@0&K1l@a&-w42klrW{VnlpC&`U8fRa>w>V_WoBYfv-#ao7@Fdrrm# zMji*-K#_*mTIc;xN}{B*AshUdbyQhHfu3I*cC>$UQOkfn|27>=5kKzrxH-uHXXjvT zxNx>pPIX_^8Et*=ZW)g&wp(slq0}imp?1?amLrUp?5MR}HTS;Ft-Z{Y#+4LbXWyOM z6{6kV2*Dk=cBGp6m34bI-r)epxT*S(OtrzJV%D8Xl8#){xct#7k9mIPlIFG5@8bo1 zjV*~{-FmBJP5XYL^Vs-jYZFXv(AwhGD{T>%oJ%~H*%SkVR|`oCdow?`2MJOJOpjhT zG(Iax9;;_4sIaPGx82cx^vojaqNu^&k*gmnP+xrubN8SI)RoC~o6=2g}$-#Z9yJBayU*3r2{dyZOeSKOP#Fs(|cOi~v%VQ3%pwsrp7hp0O zWmm@An3Y?wNg9c)U331kc9<=F#T{KomI?KK^i{{xK##S4$}9JnREMyzIT_Qi*E23h zQ?J?onkM+%6i~vm{n?GBq;QtYpEs;JbtMNZm1Wf_ad*%kK@J5Gt@RPDD#@?xa?<4W zl!eX0>MvMM=~nny#UnC*2g1)=#0Cj?ZA%K{&Xug?GxR?7$w%Ye%FF+$+h2sfX}7!B z^WM_gxVf0Sw*DX;DcMk$v`REpipwOl+#+AS4B_70mVNP? zp3%t8e%7CygI7=4*JJb-jIMVQxYESRI)tNgo;epgZZM^lA8OGmeMT}zuQ>?8BvQ0x z^I!dOMcWmUFk~X!k3poJq>t5;#QlmU;Yv_eS7T2S@PHI>sHUrk=n_Wus2jf^DC=U zy{0wYw2tRYJ??s{KBb*jodxVjGO_ZD<>JS#=6W}q7lO91&B|iM7n$n)G3*sqM&keF zP5J>lP^agZkWGVin2h2Np5;tmve!zMV07!NGftzHnd$G zovq~9bM85RuiZl%U$6TLCk_&fBw&f=eOeZxy6md#JQcY-+Ps}*dpTcHY%WOxzw3(T z%9Q79Uos3c=POn`WzQ5pUd2BH2J@q)sd_eXdw=pLnC!Af9C}sf(srlKTbizLURboa zgE)CEV`Trn99qEMt&Ud0$m2F6=-XZG`0mzb#O2{aMoF$0yKjL49WS!wX7@lkH|^hl z2^;`VR9S3gwcfvHDYoa>LdZ0Po<|gg(Ca_Ev%k_J3%$P#qp%mKjPZP3_?)IpC*y43 zcZ}>gOD#q`nV#9t6t4HEpXnxUcI;q>H}EF@Vuc)oy_xc)S7Ps(kl)dYA=6^zxO~7% z{g_%Pk{KSh1ScSvE*X0cnAg%jF}mKjZ`&m<@M~|G4Z0pP+xnqOO-G3b2O#h1jXIS! zhqn3Szb`Em-yhZ2rmq4(J-=(@7kK&)5;l6GkFP#<9d9cZzDJk+h+=eI-nhJrsQA?0 zBGTcx+@d^Evo$RCsFW?7Aw%soVS98b>iPD+UQ-M`@S0kpLci^igx3jx5a<45AFiKU z8@goLHpr#2823xj#`=)4F`xn805Izgi! z-mJX^07=g+IV``9+o@Yij(lbfpSF zp0BOGaGl>5zP29O0E0~Re0EJ`R$iS?Jd|qH|7boA(gVML}*K{44Ffkmf=1tVntw&fjS1VV`Ury3B8Yw!P$vG_N9 zpJLntFe#c$tX<6@Q}O3-AHL=Fu+szQu{ZD+U^zOd_^ua;|BM!x%>+DD>%6f$ibf(2 zPLV?GxH}R#>)gW|Ey?w>GX0%9Iwo?sSw?+3SpV&KMqq(H)ioX2_F@A(mwvH7m#Jt= zqdTM30tF@<#lV$WJ9`7rY>m`>s}>qA#z*`g&S?JP;c2?=->vq3&x3t^)i$r=|CL3D z=#l?!Mc$l^gO*@(91VX8*gK38AVk~I(zuSB*oi~o348_;|N5%~0bl?i3;i_9cWdWh zpuDRa>97{pj{y#eMdJVIkRbivPt1S&hj@1&R>5ejk!zCH8L#!deBf8qZY7=M%gKanMY{ZHVL{7>NhPnP(f z{QEzb#s3f2uxTurxY?3*@ho7;?0e|{w=MgBna8T~|8@m+8MHj2FO_kDbKrtw+Z-wS>>)N z|Hmf;;*cT@l%Byx1@(PSLT|3r_0u08t{`Sfih{36&&9`W0HtHSPXGH8=Cd2p+*atK zO?<=XUl8P|R7)`qqry*f#g36l>M%wjbZL-8_WTi~u@diZj{n0y3129^y%6TD>6> za&@`DHVqWk3OJ_PH%%*vq<>hnG5%Xm&W){UdWKW|o!jdxU~p5=SFOJO9aR5dVI zT+8_3`V1ViuD;y-W8Q1OocA6?Cj*PZZe@b=;eVg04j4w$QvMoDY> z9Ro|n>3E>~f&1}?#cx)sz_=(Ba<*PZw3=1q9~Selm8x&{MWq*uc$C`vAZJZKks9#2 z`l;DkVX#8)Ya*AztiJNCyiIOFnG@SdOqmz*N5dpa=MVAB8StSW=*NB5!|ly`qms?m z3fyrP&XrW%_>m-gNqIIi*Z*ezdb^c3%(i`Iw%BSW zTYhlSMzq&wJNv2GWGg&<4CukK-Cl`!Ma7n&-4ws01z{LaKQ0sv7JHtV729PaPLo(v z=#Wjx0&Rq+a3uXTbf7K(nQX|RcIeg$=(9rmy=9G>(52fbd`AA+<(f?-=(yjro-XxU z_Q14}$PWgX7}VwA_Cm=amkkD;^n9j!{AWk&S0l{1K1L5`x%MM^-ba>uvp| zIZw|WS_f4(D8Lkj+@C_trZ!nQVfo>8F?GioMF>WgjOf!{^W5~#V44Q3>Rz+@mt^^m zZ}JRLrV-c1wyoCjujfl`1+?kqjt5ipCivowWa3`fuK3+i@d2>9ZhQ(yMkWfLjaA(4 z)SLLtkDm{j$w#J zJulC%@_SpP;pvF0%3QEX`gvKh0BXtE9StON`G=}=jl(R4Kgh>=)`rjfQm1xeNG-myD!~Zqd+FG- zn5494Cn1aFB-i(M?|G=oiFota(rmJz7Y4x9y46;56Ha&;d2?t+@vWYBhF<&F=!Y~#dQkof&}p;Qn(TfSZ0yV`?L@lEbRH9D+om_1 zx-68`w%HDdKD@?pUvgYVhT_#|{hYOJw*XKdqldb)EUo&-U1O;By^?_r*XNN;M$gMV z--Jx#+gjlUZFT26Tfw__`9pZd9ol{b+LCMQu9Hhi7JTM(qFYz{?Ju4}eJAHizH#BX zQTMg05fyNz*(h$8?TXHl=m`fdupJUOrN30{mV>vhQm^a&8s|`ljzaFbc{`k9q_?)Y z+(t{c)$Jtc-sUbl)#i}_H^e3$_E^7_VzzkL@Y?l&^0mbg_`(02%~+?-#(Dlzp&k!N zvt4Gn#Z_usNZ%JxPcCrq^eil!!rwyh$uC3k-+7%6N7)GB6rUJ<&LlGb^;y%HBEF!p ztedc1PdoeV$J-IsxaZjf77WKvzPC6gAtM%mEs`q6#6qlgSI1dQUZp7zi0}N(^y91u zRx$-BZ#+}vE*ASS@%&d-!=fW|3w;~!o^(kG`{3#3koVhH@FF>tkyN*C({CRZ(+Wk< z!7~5n?7QZEC3ck_PUFeW-JHM$cCdaGh_@>TB;54(tEFSQ%eOSMA8(;km|ewe1e*|^94CEU!4TC#YJr8ggtLN zgU`Bn-YA-*CWP}r##oQay5sR7Jd0etK64Ii;jA&{fGvTVNL$r)niZzyn|Lgf_tt8m zDDu1coU&Gfjh)qDD_Gv`?F0?UoS-hDa5AH-I>~xG8Fe{{95y&i*7b41~kASE=l;bz7e% z1;50u0E>$X1%Gt@lsi1qGsm=RKz8ColNd!_q{_ zl2Hn15bAd~nzdY;%Aj$VyXL;p7dz68C$?Jl8BI)i?`l*KnkE8oS&Vt1TO}0h@_wXR3;7n@YS)WZO*1JBy#(gfpd&C$FB5#eLO|ZBEECMr`bG`bo+wSzn?-r~p z_)D&YX70-E0q#w7K8}6E%p$~4}pWS(wQ21-G zr>%I(JW6e*BwB&whF-yw$~p6wJYe7ZWv7U%KCE4Y5GlJBsXnKo2$T4{;FVdPo&4v_ zJ6C(aNGKwqB+DzzTOQv=vMzMQK_fGW$T;p`Vr9u4{_ErOBY%U4my{2dS~Ok}3{s(J z*d7jmff|4jv(jc8pP)q3nvQ-D@1%?nUbuOzLa)X16dhB>L0Q>X4%(eBqlYTG;_Z z<1>aSaM^#wsVF1p=E*UtWv3MIF-Ur?^Tp^olKG0e>NA?)T{*c~Kb1TGucNJmr_Sw| z8+wCxVI57LM$UGHOz5q=Xo$|M$&0i?Mf+CGZqSvCer@GK9sY9~KR>Bg0u^;ajIB%f zMHHH?ZrPU8m$Ioix02`Kl1c-u+d0k+yWaD?KTs{X+gDRD){wuEEn=?H{z2S2irB2PVVlcgy70O@us(rUZtm( zSAR|3=>Aot=vg4?IeenKJAb^#3t6DNlMF?m8jYZPxMhr%rimAa{v!!KEl=C|482mN z#Y&CA7$ypuhBl{FKTo~|ei)As*~E9v!mu}crO3V#lbc`ezcbBzf9@|QFW%1&RS%Do z5z)l8uRhLhPEdNnJ|-A;L}2aJOo?5C?aSoj%bu>*G^-?c)LWf&lcmhd;PXpUaEvp3)A!-34mr^r2%aRFuzzxi&*pU)WRFLK<aa(pJJp)`k)6**!HA0q?6+8bpo9xYeAtQ*J$d_q#U6%iG9^$Lf?jO(NE;nc5#k2wCQ z;`Q#6r&P5?_gZv?%!y86l8C+sdvzvjGCyNWlDZ7sYA|l0wC%XL)aL8r3>nK5gg$LQ zm=cGzIeYI}M8XXfanu~_4(6Z?=(0x55o=yZ=zp$6?-ZQlRTXR?h|8lL*msqZOz>>k z$#Y`-9s^9>*2e|ooc#E(-~z8XUAIXvmE}k6h)~zApB2q3(F}pL1l`khG;*LL;;zl! zR6V=6Mv~{+z!a}60NI*l72f0=aD{_S>EKNlEo<3P*}(470#f2xj`xW{o>00An5O)4)%lX z?!$4+PYo&J;Zbs=8V?^t?qk|^4#klP#ra|tr`D{_3!e`tB$kWHiS8lyo?A0>&86|G z1~b%ZZQfaq4QD8W6eji%l*nV4ww)_)nO{mY8SZEJQ&OD+O@Y2$(LboF5Ir$(_G|F0 z(>X8S6k#c6TVCTy?o!e}NpGXQDiVGvg4U#t`u%n@Ny$MbWs%Ae1}ua-rA)1X;bNK5 z!Z0f%A(J8Qx&1b01@JVM61@bO?G{y2w#DGR>W?oxfW#g@twA?kdjc0uqjQe#vi%K8 z`sP=a@pC2VU&Sdv#Vj&>m_{+0ebrF=3jF5NH`|5;i?IyH&o>ma9pgf0l&K^z@?Wmr z-h`86Y~#aOET!X$(|&D1kG>mF4~qBLG7fZs`@B?h2ov|I_!Wu0afVTNou`|~B`R}m z>T9E>zAxa(PmyctdamV-ksiG@y>`y)?N?$JXn3A~UWz>)Y90wWWh*b-HF*Nx1GcD` zDd6TD#$^LxD{}jaiMUWv7S|f6Ap{s$Va9QRM594E-##RdZ25AXP+J-aE%3XLJYM-f zx(efJT#^Dh7$&-oUP~W7V28i1$h7NFO?2rwd3%TktgjEH1B)p2%;YidC0N%0@u{DS zs}NPtasRV3&wIhKyoXrCo$eGU@nLQSY`@XJ0L)?&BIXwH!r^(LQ0N@z*~0LYYPl8_ul2^zJ# zjbQDZo;S&jyn_PEiwMJmJy+}V4@&YJ89E=BW{QN96IpEsVHvQyF3c_CgYU`ZxVDzK zxRct+n~Rdl*Y#pEN?BPASh$e_Ftmy>@{(Mp&Qm9jVW_&^lMiXKI@vm1HlJls^uLYw z`idFnuTuW{g*H2uhpvCoY1N7*IZ)JJ&yfZQ`}M{wLY0X*GfSl+OAts?BIk`?2FG zbi;kX44(Vq;ABwps9_P#t1Kq=TkOT_(@?3kswXj$)KVqT-R#-s0ilN`Lx>}zOLyPX z=mCw7ex|?W%_yv?RF`lBR}SJD`{3Gl(9MYMQ|vzi){{dCVF;qP@f)~yZl&6P`lK3| zJ@DZ z>Nk`v^f*&u^k=h1IN}t5=*nF}+qz!93Djje$hZgLC>hefPMQVDnFjPud=ek@YGKCm zZJa(ME@V8SmUnVm0zKWIzW8!+RrT#Lqvg>^_&?bOjfv~q3gC)Y&|*FW$hz%Wnzy7? zCHlbgkaA2t&!6^F`6r5;5+E@tQto3|G;u{psMA`SRi)=x1KQxnm zl_=#HT>de3uXUP1hq18HbUq#Y6mR}_Nn#k6u?r4)Tp=)W8Z1a+Qsg1k`6BWD9R!2d zycr{tED=yazjfz;2HuMuG?$FJrL8vP8hZJDv3E zTdGXDbwzt80(T+HN&azhbizE>eH`IT!Icn@$$v^T6zpVZ$7erAQEb#3rH8wn;~tnU zZd`naBfsSJ0PNp*L_YfR0Zp*;psD)v)3Nv1q=Z7ZF6W01Qtab1fYm+WhRlMfAR3`M zp;1%(FCO(=kvkl9hVC$kfgl-l{Ybss1Dd36{6&2>&++Vc&#|*tj%vg&9Qkf_UFYn^ z&MDBtBJcH~A?q3(zZQra7l9$4sJNcASefhNlFfyP8|Mj%` ztmCf8>qq>7wiDds`n@)y72|bMj{~|s=ijU_c|o)-Zu_VJFkb6w!CU>M&97vS zNBrsw0k#7uRBW03)#ES+tA|Ny*@@JX_P*4tsjB7LDnL^;@XUkaq-6`%7r})bu`~EA`2F0DdkkV&62^0QF*TC@hnAET2%ow5#)Z>;e@mP zGpM24xxNdUXlLP=cN{W&+xr-t2Y%1x8~u!44LuoF#phoI#^B?G{mb;+L5adXR$U^zj&oc(dpbV8Ip!;gPs*h}z?)Ilo4ia1EpN-r!rgb4j5ny#Z%e9*6@WqIfsC z+tZvn_)$kb(h#bi)m9tY>+aPhSMr7}PFUxkpq#YqZ>MpAq5w`KGL-X{`C@bu+Lwr( z!q>&R;Yyr?lI~E|9iaxFy7{K-9$!IBE~u~{R~o2f0!E0hx6lCJ(aaA2_Q1vo`BGrk zP9hD&xSB?^t-p!6`U=o4nRDw|gyXQ$jzyx};FJ(dBy*yMlwv+>6NG+|s2I6KBcxUn z)#kT{1r2@DGDO4|HS)0j-pt<65k&Kus>>D9`0!`Os&H;y+CG!xaB>MCwZy(TUVW<* zPg_&ggJejG5EM`dIKY|brjR2c!4sxh9<{{^ItUo$m!#nTW^wR?!? z@q=V&jnB!q1lYnp)<9Y1c$SgdcdZ#Q+_95~~PnG3f5zru2)*MPu#bC^L#b+Fh0wEKqUB7%P%mL5wMcP@box{tuE zz}d*m0Z;>TNxofrGXW!^)1ELj8=KHfzB^YH07zd#EY$BSWHvmNkzu9_%LVj?;G2<> zSJ*5jo`a_GTi_UJ3_{oYp+ub`8Q{id{^2V9Fj^#>wPbJeOyqRs!TO)bJlQ7xdtayb z9GA*&q5^O)-9FyooZW-ZUabH`%^iU!8MN&q z(IBdONaQ$-l{Ak(MBKz+*s%ZIx2gW^(sH$#7LN<5`eT~jl<}p6FichR5}MY*wI&6u zFnMXa0GFv;8*9w#2H^t?upp1^(7W2Zqt-3I)?jO??##4Bjqqf{RnNizTe=@r(ixE0{SP3JZL0Bw$d-u zN_lnz&yqa+9}(`;&BN6>7nUI>+MmPp)27v$QaLog3{2^3P+nWJ1*+@L&;24a(3<#!5>8)G~-4Q5)ROV@%ZF6yRfhkS* zG*90*+V!UavkXxd4LXPnweQkjz+_u+v-3{DUS25R8G{LvEr@e6#NS2#^k9N{R1#g_ z;`3CB7((srbkb~NeS=tGIUwsLwEpzc4pN(9oXKx{gp{`8?Qy8@l4_qM$nBN zAW|r7uuA(Eg>AYzx0ljvJR*`=aKw9jAga@R586hAMF!Y}MYUkQ?~_0vqPm`ov^g5R zPfI#^vZ)ZyU;BpT6|-`>)v?&E?qY+ChjGl~JRmVK;;uGpSM~T%Jfx&LvR?@?1Qy9R z1@bAUYC`WC9M9(lCM(nYH4`1c(@L%~%Xia~9sdCN^}FSFChhazKM<>!Ll%mC?;7HC z4)C}j`KuC;yFjtagEOuUZvTgqlFn*x@1y3{d0+sPCHe`V9AoP3yg<*DA~?j0mjKEx z^Vv#$dlMP4G7zBb&}q{$Sd50M+QfhTi}yS#nN9J=X={`~=6Mz)^7>)vr140NDVxF; zl{>f4460CgGyfsFWl+)8eHDS(*(h7fsHdt;yNPo~z;)-)OU_cYph}Gfp+b8!N1xl6 zImP}ssy0W>j-)(s{Zs#q(Z&%?^W1H}t|*SO`S-5>f}}eVn9xan6U9ZK8uCO&i@>lP znQ?i5S&N-R4PnH-VJ2s9k`+S@Nc+fo$z7ADId^!4*u*r@1yb? zTza===QRJ#XV_!+CYklZRVk~)o2Sq9@QYo?@lOBt!yr}4!w-=Wm+OPLCES!c>(fKX z{DR-hL&$_?TRo_h25D?!%FKT;0!oQ=WXUd^nD>9@@M4S;s3 zAF77|mapZ{$U}|F5*Wm0O)JUNQSeobFIP#3tCBM2gS014bauJT1~U|AKtaF&~)pt_oQ&{7>(>9@1+5(ed`41Sg!3H?Yj1cd4pIo7&kI$Cb1+fP0 zbjB~yI%$INRu*RpA29mvgd7P>BhAdc7Q^anR+efl$IIf|Wq`Lc3|Q4KYa6c4Qchy& zVeEYUWj@MfFqb6ex&)PVGoFO@xn3Ut*Hma8wq3yy<`L8A>pEd&p}wy+z3WdYq4|Z{ zhH>Fx!`FQvzjWJ%BX4)pW^KI5^siVByC7;%!}ns53Y0)^6Lf zS?J^x>V$j&NPbZac%@$e@zWfz6q?UpY`X?6Bb)cVO;O5OsKbnw+&W-6&yBZsh#Uk& ztS^T*7A;m@H;g*=o*ciBLbdoyv#iivP?0SM0{bB5{uaPe!2wBw0 zY>wD1%uH$nY~R{$LcS%rmIit?SpKvu3GeW}|DZO|I}fp;LFqT(g@RH#b?dsoNq}!zQ*N*70c}7k?UQ%x} zcue`|iNFO=O<%Yd=iPN|nnKX2=)%^-KLI}Y9`B2CtzS!{LD-`Gb@s#j`IXu=rlpIP zQAz1vS;E9|jxV@;jh@&J1oDyv{ytG&Zr<0Q{wC{oTlKM5mRxT5N;!vl)dY!&DE|&EuII&^nM0R*XP0{)V(m_&CTP!hFhhxm^xN1a` zvEuE)CVZbaF9=L?CuoYm0le zmqBX-`0CeQVZ)p^I)7|LNA2jK&?i2n%qQAk5m*j@Ds@uZ2=cvK$pJT%q-^F7EjG-E z<^!>z4t2?K+!>2iGE6H>? zSycXzM|HM}7ftH{s8Vh;ta_{tB8<5h=EyCG%$RnE`~GwPQ^}Up{Jv2p`FVBMV7&d-Vi=Qqapm2Y zblNzZv^rnY99pcG*V~FfJmca_f;~l=R0-TYK4VA}I=Kla=n?p!=vCFr)}m%Be9kaW!>I* zM;z$ASq-wG{PV|FOf$)&uig!u0dJY}!R%)i_-ZV52T+9J%;5BjzxOzJD_GBzG4r;6 zRi-kzE&~+Jxv6@mpj1~qxuJAH&=Mo89f#lUaokpXYx=tr*%orxv*5u~8?{X5Dp;`O z>kWz9U$zPeB<sB59He-nq#|4bZv7;*rjRJhQH;1@DMEv1kP^aGkKiLZ5WN!iql0b>W>2wRo> zIhE%fR}doAae+2v5I0_=XX8K1wHMM#X%cE%A3`0lo%jnVI8B_DO77Ea$-y$=XRoYI z)PClodpPex%-8%E+cY)rEL}8PFwnhx5A~@(7#oImCmh1q?vX!W+j`*6%otr4GU*Z+ z-&J-ze>s@pP_ff<7q@pB4{5_ErpMwEX2Q>S{}1M7C8qseO6#fU`Rm0!j8ovm?~rn5 zemAs9cHtmitn9_D{&WaL6a_|p$5N#kX^|PdiOteceTHIPo2ls!-{Lkf3yn# zEa9SV{LTB}2eG9+fEIx73IMY*<+%gLd;5BYnkHSLNUDR)Fvp&|Y6Fz)wRz8D!@+p+pdA6vjimUd@*Skt=r&u4$ zGXc^ZP)pp$Yd?QJM-bnN=J6Dqh`SpgG!5^>Ua|h7{jG3S{I!$*DSDgU>GSR#>>{e0 z;vD7H>P*}lTE&2R2)kg5>}W?TWyKWLhDdpO7RHSn)~YG<&FXoj%v$VOKo{a-8E++? zexH6vdmw*Y-jL%rMkR%cyTgEO5eDmT8R!Ja8~~Tv4=5vzq=wlYLQU%d1w8q>B-gc= zWoE1d5KS*-LI*^_fOcKjQXktCr#W+|TvHrkI7ZHWYGS`^c`zIl<%8&u3NU3n- zag@+mVc#S1p>l<|$#{(pSF?yXtrjNdtt%VtKCTP2HzS7;eIUM$r>7`^m#(PgQ=6W% z2J^psm2G_U6n?t=Y8}3v!l*?7Vpf;Q?W|Z_6s9le9L>B{0MhZg zzN8Q(LN@#yg~GWj83Le>O)k5J_mj}seE0A3W8v?ue(N<@EcavvleGK7O6+(L4lrDh zkqf%oZbjD)h+MM&(r4uL{8hv61@`1BzlR2}JlS(iaq&;HhK%p5L`EFROP+#dStsh{ zExEj(f@g>Sq+PiW22ih^eEMuOJPAa?g}q(%Y%PmcAV4JM?h80 znj+)3+Z#agT!{_IQDzgkC{p9zRT%@V>m=B#;So~8KTOaH)$;7#G>@f-=MK5-L=OPC z57&gelSt`Gd}UEK75#2ar#?!U2&sl_FogINrN%h@auiBWadG5z|Jg37(saEr-jPQY5 zICL%i^;>6QAW!NlN`Dm(;B=+A$w9ODnM=PbUwPlU9KmD+XGev-AIstSB2p|jUqs&x zret?X;cId^Dj9T&y@V353#D|f#%E~X;cEiGH)S3@2jXE^J}Y;R@_x-$i*aIYOdXRW z92At0v*4g+U)O|npkFvqJi187e2Sg}7ojR@a zx}~&(mcO9mJa44o50Q?0aAuJp{C*VdlKRc_9VYye+lyh)eLo=h@Q=-gcW191{H$~{ z1)X0Sz^tkN$qRgk9BNCef4-?DIMOFDZ%OIN(=NK)lx2i0fF5#v`B%Tl#CXzkE@)Zg zp70@9f2>TMhi+I*7RM~37SlY+OU!x4@KsH#>qKZASW|d&o@7IJ1lUJ!i8;>r_P-Re zJp>QU|Ju!@vdBKhK#NdijK>_UwK5z5?R3qcj=4H`+VL?yfc?_xOak|c1FvYQ_a#PY zeXaps6JNOwaT)pjW$Yul>5ttqQ4(u&Blho#@_oa*xga~c8e1Z)yZxjs=Vx&f^N%?d z5dH%gJT?{=zzL$Gbw6F%%~W{MO<(7rlJBT2G6~#@vi|73#mhSEhJ@Wds%yWkBH*G> z0^%)^U+|5$)6I!|mN{v0``T&cE`G`XBHuel{WAOV>{uBnT+I-34Qo#$+?%=*QvX?R z-Alm(5F(XyX3Zi!^FEc(5ZNS*?u$TNLJ?hZ?`HBf0xmDv>2x#CDSbqCCg-;d_pQY# z#2(V&OxS!$r3VA?c~wUp@Hqb7ep@xT`2Fr+fKz@UF89!4veduZarYSJQq6rLEs7A~ zv&VxN+Qkj0h5e>%km7wM;r>}a_I;$nqtBZhx?{$2MKZT%_EU0=-6E)zS2S?nC&L(4 z!;mvcZHJdbu@esLG5z;k%T49-W4uOqwv?ypsfxv9^8+~NYd!IgzX;BSxoGX{gP;#OuGrx zpPwK7Uf!+hu#6p+n;|oyndW1q?%B2PhnCsuPK;dp$b*|65QlWL7<}tY{a9nm^kMnG zdi@c*L2Ubul60Y)l+|)HTd$saZ*T7Km4&wH+N;y_NzB>aeD0>*ov5GpUJv3fJTi)E zVmhkb=Na}$^?r4`^h>!(Z?;jERM6#Qhe*-zGdjMNDrYp=y~N%I^#;-Bf0x<<^?2x? zyYu{z{y1a#P{y`}1Udfufm_5PER>AdSEF|s=&zV^ zdm`c5BhcAfd$hVFg*96wUXY1HNLIG@Aa=SlS;U;FLFo}8tXGl+irFtWtQLp>yHLw# zTslp2NN)0M@^_{q;OZ-PDXRn81>e1vVns3o%O@$1?J1n#0JE;p)M&J!nW~Sq?&psc z_G19&`HmVJd^B?E|q*p4-KkG6HZ&6AQF!zMf-C5ETn$j}OuKOdGb^e|x z1Z%Q)6{v_(68$~kx7?tQX!dbi7d~}UUFq+VsP{b?{8{OQULIf0Dbww9HBM%2YhPc7 zbXO{{<`2uErxg>Y-oHyL_UjAe97Mm36&Z)i5;k-(xj7Q3DDQeOz+b#Qt((o|lPXpa z+yOA(%*L-tXBex$er$0qmmpejiKSM0&v%(bOou`AZozHuD1q>bf!^kKH~%X*zFrsa zlBiX$)%xlA9c(W@!z2cEd4ttoZ)vTN^a}e|@vvbia&~}H0o7MYcRTcqrmqw^@-@-* zS+M(5s##qi1-%aNi~!gwua{#Hf(DXGWN1Pa$iD5r_0jqSL{1zQzK9)9sU0mj|7Y4< z4g{@GB2Zn$I8-@KU42bG5nXh{{%ICg`(?AsND@#`pXc3+h%2i}rpE2UALbLUjjRla z4goo3jrC^}g128HPPdPI&oil;FP;!Qxj4(fufJK_kJDKqiR{Kems02_E(Y&*`Zb^; zcM1DZ)=y(j&UuG4qsJ_muwVo@+B{~XIK)SH#MPnim~h49riwqNOl@uWeRylG?St2= z4qjuY^Snv}+oCao!ZGlj(ZX0Ia$FY%MkES@LzY5g42*SuD<}382bsjZ$fgAehD_^d zuBC_yl^Fyq1)Y*%am#JWV3z-x*2{L0(-aLv2Z+u#&e1u2=m}_!_*?HZ4|HLjU*duZ zqzW7&itm!7#1d(Qh(Gs-?^*ATu#b_4ev7dG1#HLl^H6e7SQjObLz1S8^kJ}{MI{{u zcb#3}vR4K6p?~S`=wb~7zwcte)xu2G|`C&Z9*D8ew$N^ZcDlf>(k*ftAOF^twn zx}92-4fhE9c)a4T&BVzqjo3E2BPsaChA8NPf0EP#aR^ArRXEWR{Rj3Ee?M)vz$k&= z6|;Gbz;3Eec`iU&6qc^1CwdpFnE%L5K+=;ga=X<1@6^AcAJb1{x7!{ZZIq_#8A7|X z$-h9k(=K$PIsezijGa)*5q`eXy1}o%Ii6Ttv@iYPIgwufW#ja6V^EBVcnQrc8IBRw zzk}|rytgY_b?LaLbbb37hX=O0k$Fj<(J{1$%8b-8UJ~>Q5q-gFxBXaaO6rBvY$8~K zXv>TzzX;z|I&-q5eTRy1nzVnP#dG8d;CKlJffFk=m%2~U%dikOzs?L;Zc?EH-7mgY zFAn*(@@**9%Gq=E z8{7$=or3gLL)Hvlk4S#vizLswVJz6_**ZtF0RO@i0H1S&QSl*lNx#Wy^2dGY=|pZ$ zoe67jVvYmxFZ_aIy%3-PS0g03omLn5um1L@BjcJC~)kzB^71t*_xcl9HI(0L%pU?WDz-5_}hyX)u zKCNCwmDjRgzF*J?e(Dxf@L}ScLJronK{2mM@f0BWsSR^B1WLl*tM}}@>yLQm;wgp9 z(&w&Wn=&Ny;ZSBZt^qXBO>YDFJ_HS>M@CFh35>9#kL}(b{05TEmgwoqMk1K(mXM?( z7jl9S7gRqyRTdu&D9i42Fp)Nv;= zXwSKI&Y8b6mmumw-r(o19?%5fp#nRj`*uTlud%Ow5UA$UidRKkqy&cYg0C|uOQB?g z@e4YZqml&1smqFh9S_rl|2lmc=KkNxB!*ZQMxn2(@|QkuU;qd_M)AiY?jVlYbF72B zGemxNX<~0nWv0l9tVgLJDanI>V>F#_pd58wG%ad!Cg>_me&=8HPfX5pTJPTt!37*s zoK!{~av0c>3}Ipf5ON?-#;MPBXRnaETw=#RD(*7+m8-9~doP3s{IZ4UyFe3-W6P-u zp4YzXfkQ?i1rw2|GaIQAL;C4X!cMVctpey7RPp|(%2{r_37@5|*7&Z{nI`#^r?N-k z@u?Fctyv>@-UFww0}kA)og8&V-9UMeaZOMz6F^P?d|^*cCCO^u#|`d~NqJrM)JUY6ArIT$ec!F8yeNW@_!cQf@SJr}<&pENOS~)O;6zRWG+Gg3g z)T({o2`m(?rxmlA464J|8dkzN`R8N6;wEId0j+L-QfAlq@VF?}zeZEsU!YtLk6B={ z8b**`toan@e%3U~M}g!eI{uF#bpPoTApA|1FN#G8*EB|kxh;ES3G`C&aA2Wt*2r%F zz8+4P9V_+npp$NOzg{Pv_@4taZEu_owcS8`^s$C6K|MB>pAlZ>drnmA(#eI|ukndN zXOgIr9YqQMbxVN#6XV z4(lu~HKXybMyl$r?ouYd}p+qwjN-dQ$3#a}6iS?kO*BKXEY7n<9aq2~73bEen zZMslmY79KZK8T*)a-qHFb5n^3OV!*Kb=JdJ+<_86=0~M2g^HGu|lI)BJlU9Q+b6JlUaw(g-sj%Rgt8Z{l^E{lb};ky3tQ zXTi$SdOsWj&ny)_8qZO`A~^YF3!OICi4HD!7F`H1y|>pb#6c-%-d=BT6ZyhSDp@56 zdw64YQQ>@3-hM_)@OvkZ=Mvkl&KD5pej*fN2uOZM z8y+r|FB97-z6ckHtTzJ;Gp@(64#E7k+n*;C9j90I!)}hFHPz_3mca8hu!I~vgD}@~ zz+p|Hs_r1O>eyTdj=F)z>qoz-B^yrYF$!91yCoc^<*e^SL1dMTN{YGnO(Z{%BG>ik z*WT|@-P@=g&VOtzy1<^8rb8P*&Z2jK!{-wnYE|(wz+(q#umurLXLAeyROsTz&o(lo z7i+K;{;1umKx9hv>=n+?)$cF-tKR4hR9u)&fn?LSj=mQq2kf8q@#b*+5)SN(Qa7<)9^~=m$<C6jJ0+>NB={ z$|3E7U?tC(s5pjf@maSX+^`l*jWE!7Crcn-uhS)4LsJr>*(UxDF(x_a=)um$_xJu= z>b5Uo&?mRY)k@-k6C6P)@C!f9d38G>9Wf&%z7+pA`8n;9VDCJ?8!L--y}|gKAJ=H< z%5fxoypnED%eSjpb*@Pi;pYzV8WYhOnXD|^VNc6Vld5*@4B)%I;UYo30ovoweEfZH zLKSjV+y9%r6Ocf_SK52XRCUe#!$a#*SU00qKnPY*$#xc4kkE=;4jv1Zl$vwZi@*2E z5Ct3d8|n%l1CGw$*n(~GS1gxvaruyC<f04Ga4vMF2{d}PoGw6g8S zxrB2VFr<3Kkqa%5g_kjlFTMMF7uyKlro?hV%?T9qx#H)HrR@#n&7b8cYz#iiqc(O2 zXo?fPpRdGK!`FczE{dE;Dr6UdZBceE-Hr7~5AaTrL**jorN!;h_KEa};-k+u0IX>? z0VHb`2{LBr2&_FnWNWfz@Ao2wX8dG4mg-#>*2toNwpyo!;A zRx9SanFQ*FM8p$in$6~ku~EnaA6ChUy*%2(g-3^>kED`0S>dY`bniSQD^0bUBA6u+OQpoMg?*i#45j=7}OHtmf*UH5ocy^4}uK+oz%`A?O7-ZMi#)>VrHCeV4lmym>^F6 z=8&$#Ywa3;KQI_YTb|eN=hzinwApdMC8GE|1FgbE=*vcfD&z*3y6omOc)CaWP22?C zQMU)YLM~C}9!GZ&_X&Vh&Z8UK~T#qhLfChc9CCUkP<6HQ57w4f6-e z=lsPCc{>gSzdL9n-yZa|)<#N)6EC$`D>5NHz&A=96`c8s!S+=G%n|*8(T0~eOu!oU zfY^+D&SI;fRF$MOJipS}QS~3TNC}1(K8vS}K`ht7{@fTj9PFiO=Q(%~ZF*8UP@74I zI>A;J(jD+M=2Ts%oMmDZ+NJq(2CXw?S=5&Iln@r->sp?ih7*pj99>8r(d&B_B}y6T)$4D8d6Rq5Bo2$2uJYio z6D?~s@i97<`d+?J;N{TUk;&%-*L}v&xT=RnfGaeQc$ufm1P!WaW|OQh)h}#j>;AS9 zN;E8hH23#M%{dcO4A?i_z>pTcm!m)AH{o4Z0*QY5FsW$-26+Fb5H(dh79&eJ7`GdG zQKCsb?TB^}G%4u3yi1?r7hjwwwVvi=s;~J8=xFo5?|RGco`OY`YCfBzcZ7!JSSyD5f0 zld(EocpzG_g>Pud%Vwq8q3y|)^Qd$BvRRs%x6SRRlga7N4R$%r?-)&J6MBy44^11) z@ig41VTz(Tk*N*u3j#y*N`>03NtzStQ5+o#m!lp86BH^NYxbrXriinC;b^pg0cX{J zsYWhQo(5!sDZJd$V@~UIQ7eqME{7f{Vf7J6D68>BiXt0Qgpt!~+PD9gdF+C<~r zX?Bv~aaDb+Z#1{q?)J}*VQ$35qD?f+qI2FMd>$)Jl1uXK66|U9v0TSWULOqsi??EB z(nI5p9Nko!LPnD!WQXXd+V~8;X4k#626*bcO|4iIi&A#D6_iW z_sV3!8jVOA_zMGkRn;su1&tfKPWAIj}jMS_F_8hNb2E-N$T zI!%B1P_Tb}$T9aO&Utk)YN3eal!;SLF}vt~>%CenhR$_@t}_W+w46(-9+{!%g_ zy2ROo1P0?9;lt~+A7T1ONb)4=7=O6^E9RJe9)$2v5FV`57%eodCO)*{(#m@D$sL#= z({nI=L&6ZAq)j$VBPny0hH7W5%k8OW1Z*C6QzW0vRp$-4)CXT7VY)i0);iGjI4BJ! z?~dbdZysS8GI90<(7mEh)I<-XL7K_$xfVP}2$B{P>!mJcd%5C?AaY-BgKlEb%c{_o z3!~8%TP7xKov*>)3erSB&Gh8z7NIHilXBlF@~mB{(MQQ?;NAohNd$2NrlG)#sDm)k zht(Of3vzU#OfQ+3^D=>TSRm7yUtXg96rkDkwFMFcBGTZXP)lvdX)|%J&zKoE5;f*) zE(7QHA{WF2g4nTb942mV6MQT;!W)<>0t9Wo8ovO+CcbK+1&35Y=}?#mpG~3@qUMz0nFQv zK!0}lN2&iq4mLB*>o*mRJji*QL=FL@$qXA+YcusejHh?{Oj6WBE_-9_*6T_WcajKs1?OiqDTOYczIEv;}gUhw@Zuf2A=T2MwJ8TtL!817@}H2j~9hJaq%i=d_^;dT$uv0@@EM3aE}7oib|tdpB0Ay|EH130Y&@6TT(iQN6f%}& zzhO7U*PKLmddNag>Y!(HfJ?x%TFYGAYzEM3YW64otG7!&7QGaVR*&QACx*6^7xiGg z?x=X2E{2tJ^rR?0;B{)7t!w-gQ&Hvz=A5*C#j{B% zxhJGj%c<{PQ0ox^i>le#FHI;jUDlsQm(pPXt-&jGyY5*bE(|-SW%a1Re%x&z?QS>j ziietP)0q`I5qlmxu54qe96Jt4s^*Q5=wr6N1xlh?F@z^*M0im|pQHi>L%S&S88f}R z(~-Fh7`%eI&`FeKpPS%3vgEO2YJE)#Ipmfmk^%+t?J9@eO|Zj&eB(W`nO)kp zRp8P_j%~~@l`b&ge^~&=@bKBIjs;?DS3c<-yN+XZU7lrN&Ec?Z@5Y2w1o)pnH{>w* zW1o=msC7wRJ6<1DE@k6DuL9faAp+YW7D{>w7q>0c#*H)g;+3&{`))=(4*O||{bHF? z>$8#tPac;$?~-hn5ZxNPIP2AtJP0wJFci zp!Urf%G%-B>eKt(+>wdXJteg!2%!wAs_sYc;Ibghb<0!ZDFmlY>#*gu$llw?8`j`= zZP3dNg;38DR!LF+kylwW^>fC@`<&v}v#B| z@hMEE{@?t?GV`x4F$*6~19gc=>itQ#sT@nV^R^Uf-~dysAAAIw>073R>hO@l&+De* zuWmuD4V)%W^uC51GkG;RWMY|6=YV!GZHLY^&((CV3+*p6U11zTrzQHs<7niXMGK2m z_MwHw>+CI8H1Hq!v(?+J|B%WJg;Dw@TjZ;;;#wYllmpfFmzTFvL*lzPu!!m%1Xi(6G+tZ4s!pn8y(qmmrz_`48xgGsw8(!5rV^m1*~dIB+HvdzvcPDMf;VI z{9`ghZG-_-*K9bppz0pbQNKuWkFYcd)=^!Y!+$X2^2hJMLyEgIQIOI~KNhPh6 zjW{glZQ*8GikFsVzcfZ#2%<3liVz?913&+lj1Q(;|E(us|1QM9&6?S)iG%jKZ<1V& zU4w<*e&Um7pmZ`To4tQLby>tc)DRqb1bJIhd7l~aSe)p}?jZD13e`76`Rif!uNAnb z{#C4EXx`Pg?mJI?JU$E$6I5QZrf;>ux$f&c1q1My@&v3=M}R)j(jQX zk&>G~N-Kgn*AuO9#c8#=qeePrA^)_~onGD?{aHEkZgy40*%6&D+)mBMnpnM1Cg#0F zR$3LtlAJ-RwG#^!e3ZT;uX}Re#chZ7z4!f?g5oH?XQR!T4}~Ra_uQv7`^wBR(6?bu zyyFK5MjGOKrT1e|=@T5%oj_JHvJTwh@7FLr?s&7_1z$vyw-h@)Rebt&U&h^PX%M+| zIZ{Q%Jd8sz{Qs;*;4^w;$vXc1qxWai9F)q3)Q>#89Hll=O?EDw&Rh_vG=)LY7Y zP_}u@>`P7i4R-VFB_-Eae|H+?`$@Tu%82#MXhJDVajHWeF7K`7!oO#WCk`q*3yMtW zeV*`Xd)4tISVRQ~aN-?1gYs_(5V768Z z3rsGE#OrvBunqR|Rh`J(xNDEOud z;<__KG*_lWO>3Wi=85BD0EZ!k_5bqcXW0&Xtp3ZN7aw&VDsK?#=0oXT!b>fHk&iiK zlC#cr?_YBPv9?P;A)1LvZoJjo2P|Qkn`PC^9a*9#sXSPSFCI?jSqH9{W@KpupmF-I zK4M0g2mMdjSv)NukLavl>q2|h@-}QY^I}mL&5ql<#V`97;N`Fiv8#2)Y=`Xm66s5o zf(E{?^j2IeAZz6>LJJj_wkDX{cX|_CukXX{2J;yCTHaOlbVvxnVqxTWPAMto!&2?( z90S@>4ClLVdCDRD8Y`;ZrdV0_u^+#mDwryFP|<#Gw>rl)b{GAa%`OC7C>E+E*wdv+ zYmF8mzN^b_5G+1{t#CqI9|D_0~oRqpN5 zm+Jx&AFz@dR1hplum~6WH>bSq$<(39(S5VS%l*7Xe!A7R`St?)Wc;=OJ9Y!m?tt>3 z*Fe{EmYA65BH{s}d=1PcMgX&vH@n6-#myLqx!0;}of5gK@Ie`wW6rT|c%XEP<*m|3 zNB)zi*bb9LLhBT;+$bV`4<%rAot1^oiFo&@zBtY>aawsf9JK`m)B5SFw684=Zet04 zPbwjKUF{Xumf5tIe&=rrXI5N)5%n9pY!&y0L=fylkX?WB6LmAZ;jFwNXWDS7+lads zc4N(E2FZPr(2rV=A?N2sc4n>FQb|_0S!L=;rdLSIlx(T^EIjIAmI|1XWz@}^V}dEU zD&-Z85H8E2Q^HWft0w#r$937Q9tTn^CaOl&%c_FoB=sCMRlCmD7p#s7ybT0xv%Lhc zxFDIjJ#|dSj1aBoM!@Y~XjN@L4#&a}p2Q%!Unio8>HnFtx~h`+V`BZh+VKWr`#Kow z4x{5*W)#h2s&-eGSO(oA}^UuvXZo&_Vx^nTME57*$kTo=_%wLMlf> zGdj_N`S>R*>lT^8^?7O&!At=HKBCpc`Dv1JDkD`??YB+c4-k($i9wcH#e&tJqC48X zI+X0U;SGb@LCda-wr!`%c~|h(^mj9BJ$Q&G-9%hQ@4R35C~^PvTS)*?sPde<&BBo%pj zqkodvjYDy}5uMvee!76DBPbjU6GS%yFGt_`JYJ>Jk!tK)Lk?F**G%ESK0hQ$GL%^- zc432L!k!hfNJ^o^kl8D%+2u_tlKiM97D`odMjZg!;o5xJAxO&&UK_cDQ(gmd7w zlZ5#0?DxSZA>wvl2Rf(QG>GDNZ^Pimbh!&PVN$JsqE?(tEj!75jkvA;4FYCkOS1S- z*;SsdoadnPmg(nXRo$<+D8z;@!F3Azv{$Goi43!!@w=;`lul0IsYfI0HvMQfgzW(7 zF#)F?L_`nY=^wT``+HaX`_kbKmse`|!&En3%tP!y6yNyBiAH%{v|lf&Ey%D3s^V~Y zXNv$I2GPbUC4RsgT6+=4pn50YVRo;FJS{Tvq_;_Cj)-fok&_uu+N>8vDgW>o7}ng``hQC)eNXLk%gc?NCxGr}mh`IKALfk zY5N4q&NU~BDOIcjsn#%R3`XZ)GC9EnXgMNVj%&`7G2O%CeN^^=SAqasuf$`dVmXG& zC$Awx=Kzi7h>c$_589O(ydG))yz)qORu-#3vcMhfOliiJarkZ?Xz?!ymkD-?(|HCW zdW}xy4ic}>U^_sl=5b=2-_aX*54uPN9&;K$5i#_n>!<6n8Z*E!DK^^J0VHO3ujnP8 zDlB}Q)0LbRV*?BmvllTVVtb;%SnQ(#_#(Y&pD~}BeJ5sv2b`hdKgnhroyew`<#lF9 z@oq&+fp;CAl9i8A9GM!cMEBGz=ImY%us$AYaB9LKi=bbzsb=Sf4W;iWmneTnIW*Y- zg!rezL^*)#A>yY{pf>IjtAH{3tvm7AU`E7Qpb=>90hNV`Wj|YH#|bFI7e`D&u_L88 z)^<>5N3vPK*>Eq~wIlfZ_bFt5a(CE`ITE%g80Km8QO z?Y1Wq?ecCl&A5%G3-MAZSAltzArsi&*(JDr{vqP|=u;;v(qR5vZq(QH%Ep z!L$goCbd*v6Woj?%S5;!{igWoZc=#!E-<-mYx9tr24M+IF|(^0Xx;@XESQI3wY|j^oz?dV5sCCDuluBd`iT62UvtIA zuDtxHMH&6vRYOz1lV-5SERRI-qPE+VWCqH=;%+jkL!f##7MY)DBW86VxOH^dN-Mve z&)$#~+M~_zKpl01tG-%Tua5Yv#L0dJ96V>5C!o zj~^=u69s2%z(YOM)m95y3ZvwY=IodpEmZcv4aD3CV?#1bQe3>r(M>f%cSM=m%n_yR zq5tG3rHaBKW<=uad08s@<(>Rv)7q1hBBOPk;P3zqSJ%0cUF~=%`@y{Ah$GQ(*F*}U z-~P~r76x*4i+FdzpHkgb`wEL0bQf-p`N5K-~KIRCuKx{*h% zFu0r!ThI)B^gBP_##TNNTO^+=qh8OjEXK?AtbtOJ`J;gQA8iKTHS(?vQQfiDUc@;u zZr?*hT$}fOGB}N#G}IC6@D!p(o+w&$Yy?1yF5&Yu-r{j>QsHOGDh{c zoS5BEE51=#9Em6+1LdsUH%~(s#s^kxIIs6ecv;`~gw&!}RmO=|KT3)*WZ zWQ+*-jlXqez71?%TbMQ&FCxS!%jPJ(bd?(dbo<7o4*y9aPs}q%?*=ZHoRG*hDX)b9 z^#dJ8nJB#OxjRpEuVDn=DJcx(J0)zZOzt1_u7Rsi)?6zLbC59-^sxM9HycFud5ECE zO=7G1Mk;ZmN`*{2!5Ag{ts2T3H15ywF7lr+d%BiyS;>9{W8}kXp$IUh1>+W~uv#cy z$2qRN-*FZB(LOPDQ=VhAoAx~;bA-gfC!$K#ol|XP3`uah8nVFh%VASQEK7~4E<8Go zlhk;r@copO)A*k>QXDyQzPZ0SYvF3a+I0(~2a>0J4Xa#T*VFL{c>uekIM{bdqery?7c@v|gX8iZB=XGLE+AUbxB2 zo+P~l3k>1qI-lrGlF=ZF9dfzLyG>>Dy&kR`rM!p0d7EEKL5Tul+3$s|CY+E`GoUre zU=c<|9tLW$aixGO!M*6owxoDmB3ClBu0%n$pS@a#ih|}JMC!7c9u!g0TZlfEkgLmx zzD&@B%97$wY|x&58g;u^PXL0#$4mF@w@wnIOYrwS5A3THb|sfk{RAm)C3OEvv#0Sk zm-hm`aqJw?FFI*8NxFcx?ySYX>X=3P?m_=}J6cYhzaF?NFa`9G)jCm`LuDQ`l)HSk zk+1=-g&HT46R0V>-#aP(?9r}v-xuhj^2eI8j*2@rYeH4F&_jWGF|mxtLEU~3)$Zem zNne$U)S#z}A07G+4Z@O3_m9kxcNDU_1R*5sPGME=_?PV;?jaBJ4hG=@G2S5G<**J` zm`>go*O+Y!!xi#B^tOjqyS&YIsvY|i@&BJrp3(TuSOLxN=Dzd>ywtpNRuN_@!Xokk z^g82}xI@O5ILRB38R(Owl6ta3$2TiB-S-)Rh#wmbeD9|z+#QG;NRWnV3QyWucB_jM z#VWM-B51Ssg*TQAoX=G*x73uqfZ#kPk?-oxRK65i88Aq*L5G~3%T&wX5o-;w6kw2(>-V{ ztOw;VjKIzoPkCv}>HQk3Ofc#`6WOX3so1I?WFde`bt+IX8H$dDDE71f&$a|XVof#4 zvU^b~viw?R15^lIu~k{`NgmPH@y zpkj5>d32krFGooAMJ5ULDr8cPNe_`UywOuk=MGBTI|o;uOT*HQ<5pYxdFH{V;(S)i zu8(@=J^ckrhO|K1_Zq>Qj>)L`CV~>xZAOnY_s|gg_S8Bn`0Xecni!wVn@vB4dlB0O z3?2uS@xX@E2D(;OgKr!|vKW0lk#r<999+0dSxD0C14^G7!NuGFeaYAy5qpTwo?eao>W=RxI7>(-QAy$9zF}2bB)Es zEbp}7JnFwJuk6d7QHZY^deDSg|J25V#@f z=-w&C(=aegN;vsNPjO6Wf@3NNR!Y{aaXx0Y+szT`(aKD9Zah%CJoECQ4YfAbA%Ri} zg7;>>W{ty2Ma^g}JFfxs9<+#c?FVuByK%vAl2~8jsExad>&F?Gr;5QVQk0paVBur? zGyr{*AXJKa9l>L9GfSM601@(upIs3IIaUdwvgP68BxlX&`5e2LFEz??JIH74HYbYy zP|SDHGorIC=b62feYc_9k>wH0CKOSt`JB zC}`-NAF-AJ@Q3v?&!$4MUUo3rjENTlRK&tPjH~ng_@ME$_QxA?yNaS+K0H3Bho})6 z$GKu1j}ajEC3Dj&mrm>#-x?rxS}l}mWRUQG%U6?stJZH~RYU%i>*?qXiAweq#jWQ; zyho8ow-x7WPe!VyXBJ(EPpRF7-tcauvfc7<%1v9|GPoeGEY`ls2xU! zr59!Qay9)zfa_;Jsn(@P;h^?J5+k)|K%t?KHV(XGtJt-GN5PjCL-meEVK>#;`}Xh? zQL89plsnZYHy=Fr_)e^4q;zbY6{3uytmI&T>e&=RVqUUngcG3p8SEK@@%)2yEOMYYRVyYurqgOq{eIpRun zfn1oLsb(;aS-5(zHA|^}kPH^B)1 zA|8d0*95qUnk%4-aZ_p)IR3jMS@wdIW1Ul_zka#*wRbhc6A`RmNtk5JOj|&c>x~Xxh%Bfon2K8R*3)V zz!vti=NpZNAi!rgs1T@30DxgYB0Hi%5mea>@nGP5B2Q3d;h51%?^;4QcSt!MzWU72 zIsp-VP1C{8PuyDPVRO`PQ?E}4@qT69h`7<{T9&(y!j;dHo*cJrVw4OL*0n>o01*%>SyGh1P$+PEG;cEdAP(Ld)@>@UGp^ zOu~v^WvLwD@_8`NUAS0(v~Tj&3CH#nw>|)n^8~yZu>v%y=72bZM!ZQoCHk>5P}DSySuw4A;H}V?%o~T-CYAAxVyW%yG!G4 z4YPQ^v*&!frgqJ)Ggb3z|LjVod3vqK?(4qf<~SwqZ=AiiCV*sUq>bnMNu@Vrc!iQv zPUM;iM%O;8e**;J30Otq)y^xfqYjXn?twKz=nv{!>-|BXrG4d$D>f24uufQ1{!IJh zFS+*0RRhG@u7Z${$wbRoF{1qj&AiTyE)rgSW*VpTvu4Rtx!9vELWw*3e6MRq2HtM_<%T2zaPg=B=X|RDv0q7p+ax+)RmmsQagqG12+vI-w=P{P zn1JDnxyTtnGpM`3{F(c0-c9k9eIFkL2 z@F^|BSgOsK-0_Bwp}S;~OGH1}HNh#7Q6#kOcdtRom)@s4*HZ<(M6Q^%5*pc(tuT;D zyM|W|GLi&$j2D2VczxZi5@66JvM<>byi*_albOI1M&a-kj$VkjI>#73V^v&36xp%O z{p*7G_<}XuxDVk^+Y1mYn=O;v$?f&p$U2fuy_Jy3t~DVE-m_c*AbS9}VfA)XP)}%( zhRgK~pxKU^*?wm^2WUw+2Ax683bvF1<1!AlT6OfIk0Y_-b;%`uClX8vfE;Uh5fxC5uzDn^nE*32_8V30^r1x#@l!{ zVSvQJ_4DtSeSfWl*4tRwTMTk0^wKv;Nm=XZkJN^D) zYR)eX30!O*R5*FHi;qN{ro0mlhj_^;I`T2shvgVx@pS)lvu05!aoh?Gc-)nYzV^xe zMs&Qn_uaeY?G_ADhs_OHxTC=b4ljk|OT$cb%>2Kb4vu}$i=C!e`(|3;%y5*Mn28!W z$sW0jKKTBHNI_FfC*%(Q)ZKVv(@J3~f7Q(RcxhHM&L34^5|s-&6?(955?UBDd0{!^ zUQ+isk$M7$0KORJG=Am~ck74vYgvX193YnpT0T5yTI^t%^b%pZf~>zMBp@;u<#?e1}IGyXrV{1RIB8rVvd2~G+zVw3?l1rJUh zqm%?-f+qWhe;q48b~`7)@smwpD}F2*jIbw`%!d_~$3Ef-q}VeMfanmToQEjoJ2Eo> zBGQ#N|0qd=0cahE{E~_SK5sP9=!M0zjUJL(td5N)BHW#2k1LMcadoCJ_ZFqXn@&5N z__?nB@VroyBf0Mt;I8lyg|$BXM;B?!b4;~bhZMUrUS!(CA)=UF-pKTMuwr=VH^BepI*DE3%f8*PVXvkBSQEQ zgBLTT^BAC>t`XINtIVd#yx^1m;#(i0%}G%j!iu?UWtAIecqTX2ffqLAAJPDr>F+De z$ZB!`u?bb&^ z2gT}+WIhIee$M{=?L#1p+(pEdv=w~S&$gPtumvaYJNag#VY#txrmthdrDlec)R4r06PuyZ{(UsxlgM=O@?Q zM3ASV)6Jt&4dxi{JT9XfW2qhe@Z|pTwcA^4mh+-{#uUJcf84U~4UH&(%x0NyhJ|oA zSQ>7B*w2Vkz)^L`c*COjeuH(wTpX?s7qH!KhI9&TwQuY{oHSeasKG+sHDX4Pxp%iW z2;d0VSXctE6-%FrY9#+bB9Rpt&c>iLd9-qg?I{fP+X_^I`=mwd#(AR6mK4|P`dxmA zp=w~y4kK4NVg}+1qo_J(q1v4L4n8+-u(YlxChfA((GT}jajKk3KI%(!4*gF~mp)eo zWAExU4eJ(Rq@?1Sg@Fgp8-4>+AAvQ>o=!&s+FO-=faO?QpU%|T`P0YOGikU|DM~LcVjTSxzZeLi007v_x zg&!RhKWc0x>(_$8P0wF3gQ_@74hI-pb@dtP=kO(fiS5jFX*iANIey7OO#I^y3oVfk(7cJ?8z3ijP=eO<};- za*pU2U&Qmc27U^MKH2?3Vk>}*zC6qBdWhO9-*FEFbA;s?%(X?GD|SZPRgHNO<8d_> zvPOb`#g`Sp>xpW=zt3vyh7#N-8FO%|!KlCSz;iVva5`f5p)y4yWLjSw7v-UN5r-ai zqpGaM(+$6msF1$itR_w{msm)GHLqFMX~9gAaWsSx?`OJ8g=y0b7EA);^Ay2SeM(5< zqSY1a7OYD)swI?Q#US&?PsJg0xduzG&VC?M z@9i?1nCdeBv-*vtgWWOzzqjYV=FoS*}m zwuEg*6d=_-*fe3*Ua?)I&$EjgK=dS*>a%eP7)v41#OHk#^!}WbKq@VfkzVUQXz>^N zH?FdGw3FS!s1Z5DwsCZA#PX;}c-oqN!Pu-@2Db)}t&hQlTJNjAs-B8HBR01Kt|5Dt z+TAzo`$?3|-87bjSRX`?)GCe9SV50ih$yKxeMq?omcNy-ip#&|$OFM36Dk+0ohq?=bTAVX?&r&jJA+nuXGqOXRa!&Q1P&j91aai zmp5~oeoAso33H%_BTYUno#M*Qr460lw7vcnxAfz8IGq{MpZrT9EPgAm0?~qi6b{>? zclU2E86)A=A0^a}o6K|Hng{P8m7l$`TQ9!$rL5vLE0rp|vW)z?;LO!GiNAqww%ics z+I{^)MaRAQV%AK@(=`tg5vlm`Zg(>RH^nM|*w5mPmFJ96@~)!v&|UP%>e-6#E!rV7 zM(@Yh1T$PC1Rmk8&?|&vXNFEnX`pg(K>Xut@v|MeRyo47$JnLiHjiy(PYE*TMr?q> zp&uTmMZ1jq?bd@%iv6rp21}b3!Zg%pZEpH_S+-snMDi(dCLQcQAE?pNWWE8kTvR&a zfn=o?_Nx0Ur>o2N$?xHkeh%$Lr5B+Ne5EgAR47`a=#ig?hlx6v&ak zAX5bf3Ih9LlOqRQmydE9H8H5MM*UO<;#j za33vtwB!&#=){D5*ks;45NCxBWHW&4T;oFwfi!#8C~d)r406glB&{m~vTy~JU2I(7 zMiIE)?3SjBn7*=#ve02QHBFg-eee*>OOe|y+2@fMunJzKI+;IT2BI0H9cd~+7_N}( zb-ec4pR%D^p&&uC z^FFa|HDN2DFj9(CXB{5JHpQx;pttsIYwnRGc+}RBX081(!!v&)v({SxZgcuit# z6_qKq)IBA;{W4#7EVg_J0o&G6wj&^1958;maOTU<^?mrmlP^+K-#y*`8<6}=_j}%L zC**nd`oKik&oz041-XU#q8~L{x7z3Tkbaw}Nq16E`ret{3c!LQVJ8iTbvIufXac!@{*x1OIBVrgT+UpfXp5d;e0E6zBHr3{3lyIyoAhVyGTY_$2TKzrWJxIb+@O`;<3gh17->+kS|Kt$RvsEo?qo347F z1rRuw$9&`!$l+=>1uh=9gX6q`Z=I??GZLFf4*{xoK~(X#MOpfj?YuMkO;qYp3Ut3^ zh|F3T`{7iYA@5sBzv~H;h7U@1h-d=_`BUOpN^3~lK?L;FwwKe-0_D6oDAi>%rR7&x zjFFP03q@0(-w3=@DFIrafu;0zYn<<~3R~|7=utO0Q+3QV)qxl!t*>Azow7fld=aTw z4;HPBW{MJ;1mNLms3fCDmiw5n^om72Oczms1p+dWw7EaZ0oq9_p7S_ zEvfs{&WN>b0%Wr?E4;0Z4b~~E8TTQp1r)o;YIMlVbgGTMH7*@a*5q1F+;?H44(l;I zxWl}97`^s_$IWnYX_QHnuCM1V&}x+?aTTgm#5C6$N<|T6|J1svfwT$qF^;kVAt}Lc z5))^PX3ybv!s!j&@4l^lDXpo}k-i@5Uedq~5hDV!9O|?h0Sq zEfxu-G>(p2PV?K{>);=e2jk&ur*Z(>%=sd8p0Rmrn13wb*Q?Ur5x`gzXlP5n?KE1K zA0W1%H$bbxsR#yc4m*sUt*&Yv<$GRdk}D{Wq|acZZH^?2j!{JmuZ+4$q||*W_jBG= zYW&+B;k7aJBgG!RvxG3-bB&TG0mVn#D{GoDJ+osJX2E`y7Uk&OV2F;Km}YZe5-okl zC39!;hjd!4FG)`V@KCqH_y;@!dd4osOJTh&ls#Srf#0o?YhrhM_SZGaZ!%IIo6NzW zG`SKU3A9PW_tH7^l)*ZrrBANteUjL;KQ3E%E$;+gif+#RPwy8ni!Od9pO>vUX^BgP(q_1<50-@_Au}6xQ7^PIM{~tneZae_&k;_5o^6!B1B*?%4V+ zUj&}Sops%NGD-0~X?rA9b0#nR9(tBG?=8~uDW1&0OQJZ?tJ!C+Xa++h3sc`oG4tKG zr%2?8ky=%9zf2>^bfxM0^oqcj>*dCCSDXzXn0t&q#f7XRJrF6ryhCEkEWC}D&QZke zo<`Z(_a3HaGbEl-k)?$9%pO_^)MFgN3gb|Og`#*yrWu2`KQbm>HOoM1kX()yo)yZ3 zvB+Wjx&c-6=C3;U6MS>4I%ZWBWKZbVnKv72q`M7!AoDHh?TlRVHwE^|g*s2|H*vDh ziB&~x7dNxoDb@Em;*0`}o&CgwLDCNkDlw+}Iiq-0Y+U%b*wT{htT!x7C@f zUq^7r`JG_r3k1P26Zk7uiQ=#Jm1L~DW4XcuNkPPQvx_dlw<56dRgp(Es~~q1Q=tfy zoD)1f9tWy>ZA{LQ#5I6JmU@YgFVv!b?Bl`vRBoGI@@9QwqX|nIa2fGOfX6tSRRO0( zhbQ&1qN#BNDX0iazVje&Zj}#g1$&l%SyfqLhPjdsR{W;VH6S+uiew0S;T`8<*x$x$ zR4*{n&8>>h1uut+d0V6Ui~>&uNpnW{~q&_lp5KCG2Owq3EeU38V?E zb4*Cx)#)F7QJC>M( z$^BdD7Dj1C@^5$aDyf&bVY}^u|1ja+qO5-_Eu-D(8H)+Rny_aeR2I;8Svo;6q69#1 z!wK>W=PCA}k%BM(@EQJ?Cv(exOG~Alwc2VU5GUA~e5f+DG74Qy9YM6Wq<;p+YqrDo zO;9rjH0k$o)p6eT1iGm^#{(D@J74XTw&AB_CbMbqa%8u{sy*uTUS0nELgn*E+yA$V zD1iN*{*!v8Oow_5q!qS{MCc3Yo^Rpu4f5Po%!ZJ^!`-q$zR&3`!oDR(g}d~@2<^u_ zcuUdSD%GT^BA={nM1R;9)mc9wr8*Yn{0`^lO*AKki|XMOZ>zX4A$mwanM)Bi9}ht; zRb;VGIQHyW_KJB3(%nQ`116oTDblBrU#v4UMUII&`}v()+QiN9Azb-aCzQf!Dwgx4 zv}3z4-UX@}Kpo_vW=!AI!e;(=-m3AFx$ILNS<&>sqSdHo(WI>|!afGRD#=HbR$9MTR8@tE zKS_2336_*;zb^dY$JH;qJNOVS{A$w^is)X~!3LAZCEGg}a=k??n)SVV4u!=&@`9Fw zJRGbPM)1{j@ib*PODnh!-|k~@1{2jAR8>}AZxvq5uhvcb%B2zUOdg8?#52=JhO?9< zZTBoaZ3UCYhQEc?`E;I@9+I^w(<93x$?pc((FMTj6Wm2+*(Doa(8Dde42eRRULtIS zXBU@mNYHZIDHEdKoRiWyIY8V@d(goJDp@7AJ=wWN47K>!U9c9hd~A1^XJ^_Y0=pip zmiI$NTdH!t-L}O9vr#|{0C}k}I!h+zLOd-FG++!<;)JUJ;w(er;Q$a4axA zF%6E+XGlh>@2`-&b-6c2*0T~n3GCoEyWd>IYVlyGI&>L-ksp(A`FQu*cU-U23fTg# z4<9=788?Gibr(3IT+QL`=miyCgGwhV|#lmOIl~s)uJZWFwu!Dgz7X zVGiz!5|WzR6SZ^P9G(B-IJ{5Hf#*&gv9Zf|!oyc1CpriVqXuz{bl7%Nzpq7aC39d^ zDG_H+Q%rT&b=(^7HKI$`w+DydB-zJsxk?1_cB=FFN5Lj-%6!yyFV+&m3Dp5@|2TQi zW|{VFW77?a#tGENBsI76{vilcf9yHZ7&+EStjkx5I!_r$LX<3}ty2_GqyED`ZdLRNQ(JRT*S3tlb| zb1-SYFZu0#Ow@ZGoIQ1K&9Q9W9kLe$Tn%!WFPtH8xKIe&Tl&(~*0kH-wyLbj?R;pw z+(1dwhR1)Ep3X(x`NnF@|Ftv<-hHk%WipogHPZr3vhQYa*fCey*Fl!OLB__zPbOE| z$o_q@F(c#{s%H(m>Eq#<`HOHJ>`UI9JI0t3>j`2{!ixt^b5T-OG6rY008ef+^_eUP=`6;hcj1`SUz3~rF%pRpW`%%2Z zOYGxbLGa_2u#4!EZwrh$kqFIs%)n$#201|2=2dt(Cf^(+iRF!vV?m4bZiMseydLda z2=Sp1-N;I-`fdNB$@;3HW6EUNdakp~XET$!484otx{DC<+e}zDrAyy4j7H`k2SJ6Y z8{YLja}Oa3xL$%q??fp)*1M_I=12(8XEKn`_?FuaDxH{g&zhFSVCp&ggq8Vvqo>%Z zH8&7Ed)?&P5DoUeT=LLNz*?^nIef%4r0J7j5zq^N=-P}h-rw73!78|rB&KKKT=6hp zyMN{2ow%rPF~P8(7!*@oQdeQxLP#!nz~X(pdEnqk1f1hUmJ5WKbK#F}yCR-CRIGpK z+XYSKehF&6^XP(Y(y7&sHF#3YE^VHXMe_vaHtHCQ)0eJeZnVDCE)o$4N{RVG~#g1#KG-95ZGSSRGxtj9Su(E%D(>q6lkHcUf=x9bUaEo!D}XE=359Md$DLxE0;nY_4Xx#aKWmYx;{fO4w?B+ zc1!vc*Ykq3DG*j7e;2k9nr|{4@4d#qn^;GI{NXajoDtYIy~%VqrIoeBQC1#1UIFcD z1t?<4e)Bo!TxIp=cmMtjya{#?!s)AGvGvU<2MsmIA;ws%$rE9Ea#+t@T-Y2-jfz{`c1lWgH=ckLN%lXjCwE_vorX7|H!Kp_$TLo2isn9x~=;6}YvS}s4QG9t- zs(Dwh_Ozcr(t%@myo}H36r!cK|M)}&2nhxmRTd{1ogD3W1OIeyXKr)t*+_8LrZ8bJv{o=0lJ=P79=V z)wQ?(_1-fOhtvNnv=T@QPyYL(P-2+Ej*re?e=<3B??<2f^&O2D8jCCrBYRxocPDbQ zK1E2f&os413Rkuhkfy{@{d1OOai$25CIWVJR6t{$15fXQTY(_N&6ft5!jxB)+?#>I z!bjCs*ViuRKr%Sz@sBI4PG|zHa*2c6UpJgpDm&yYtI%4mWhA?@q4w^N)*oCEDn=Cr zp@$03iv!^>^;?)w)UkZe$1{D(-PuBwSu$G)*WGax(iYsFEXUu5sKxgxnklvC(?(jT zjI=BWhSsUDfJ9cOF$^a1EEnpx{w4{B@^x{ACpkwX2^ztI*d~8+!$TPwQh1ioo z5>P87=s@M^?E5HkTt?JeIK|BsEKsp8(0o0pPOQ`lbnQzO z#$g9}HEAY02N)ea@cp%1rM9a}?yR>B)xNH@DQ>--FLfc-nKF z+y!i%(Xn1^zo$XbGn|$Ub*s1ovH&S=$w@EbJH9lNOVxZ7B<5 z7yS;hZV@>eKS_hA?q9!@zin6KeMJ>#l?DF8j4ga7WUf^n$wVBxcdXNngb?_G-w|!M zuNAofF*J=ZPN;0Fe-X^vz=&5;8ufiwFOm|iM#6O#0sFC6K1$HN0d&~yuX%m5#>DQ2 zw$6WFue=L4?=3&FQ5{#rbUb1*rt2sEuVR?41zRdbMm|C1~8nj{9x%P#m8-9no>5T+7d zrJVRPxqnnX)WWqoRivZ$6D$NCwk+3S3lJXp3KOh{DI zUxmBfX;SpR{Z1nSKvp$)i(+^c)aDlWYIbR+GeJ8vLX*gmrW*zqXbgV=oc#aysR~Sk z|NWGI2fhkl!RI(cWWBq?t^9d*-Eqequ{kIs6k!+kD(?IC{nhz;ppZU%IK(!aHyImG zF8S6Mz|-_Lni3f; zz?R(b=gc3ycm|D~w;gX?rm;okBZzw4&Ri;w$-WR7y&?5Sxt|MdemD-3iyQbc^m1Gb z^CJ5^&pk+{G~cYqW`D%&v~JnvIB`x(b9U@z>LBT2 zRIvBZs^8V&WuXbc0^4*b-py%F-iq+P-ATndx z?BU-i3Vz-deC|i?#7Ld8``Ie6&XuXUyT z$Sf2vV@g&Oxa%rXhsNDKJ)8_HYgifq(XTUX7IVKmJr%Y-Nd4ctT#+L3BM{QD(+Y+f zaTI;)23T~Co^B2Tw7?!_^d6?9K5iVhHe9)}3G)370xvKNA_0~6f3ZOR(`xzee#l(= z$A#q^z|aI-SVFo^>7@3A|L@`b$1M9#Q|`a>;Y~OYE;vr|U$5r>?)d(F2N3(8!2%2N zf1d1ro(6-8sMf9E!6L59|IWnxkL$&MHM2a}2wHf$d^tJ!9hxwl-%>O*uPyaJ#Nnro~ zmCwMn{T{j0E121GA9u1E4ao#G+O ztll3$mr(&xB6kk66b5C*n*FEJ$ts8MP&k4B;FW<6x{LjCHiWm72dh?v* zTH0)^T&ix_^0b40k@5U!O!IxNEtP+ZP6?3Qp9q7Vws9*D27eqozhFxX)jyK}vx82L z>-jgkwP!u}z^!koJw&H3B?ENd|DqaH8jPR+ebMeWlz(s-;syUeIo zG4%-CQd#jW%Dm<+YwrX=EipkKO&3<1(;uwSCy~K ztyLDkFIrU6fm?fDf+L?EHx+mP0VsiQ9s*cyW}v%mmEGF^g#z8j{Jz)vXq5N&lqcF{ zIGO$U;Z31}S*XtEI|lH>5oaY}337bqyzHo=RPe2viE5x)@P7WcYyw_Yz){WOh(*nO z9^R-%^a(ry<$eZfPN_EZheY$4L1!2To1@bT_aH!R1mcPmFMtSwdKxKWwdAXQ6(_Yq zYXF7C@bOre-ob^0$VFi^ZoFX(J1b-xpd$y}Iti#(^)=n?h|r!qS=1D(O-}v!+Id*1 zh*G(qm-4=XYDfH~d=}R24vyP-VZuJ$m4xa6GJoTr08>;V-G7>n4c16}hvw3rwY7t! z?ON#r!RF5SQSE9HaFt)>-tAwD;%D9GZsfQjwjUao#L)!f){cZK!Vl2GLN*?0kCKI4 zJyp7|G+#=J_XoQ9Z5f<>*I= zbNXu(OOfAl#dTK;w^VI1kKqXL^I87nVY(3ubFtHGjHA}H)M|1RtG321{gW~c+8 z*>T(NHq?~u5;gK>OJ3{@4@oz>H9W4Up60#4*-M^Nf-a0Mg2L!1NtMRUTi%&tD*(4u zcsx}n`5G(PEc_MKqE6WfxMxUh2Z|x!u!{WtnD@9WXWn%;Z77J}JrSL70cj`l`r*wG zo7e6^X^Y+HuUr5cil{nG1|k-o$&W)ATiAe>+HzC>9N#Qr~gSJL$PBEgsxhA^2~Hw`UP4t`|C-Z8#;O4^DYM3gey5LElOGv#J&pqZ{5> zS+_#Ng~8k=$9%K9N0Zt0FC<3p$1*Df%24T~rg&7?58#JO{$Y;_4_9R`SzK_U@E2Er zLqF8UI%0R$;2_Yo1TgGq$>`XY(RnBCvb6Yk_X~ETTT2!0G76PHc=Ki*s1rqNe@P)yPR|NJRMjaw>#{MgF^dazNb7d4?lS{v?FQRD>|6t+!(iHt%IqkKX|iT zn;GPCldWVC;voV~_Wqo>3~bg-4WlKA2v0a-_8f$7 zYsjIEBcLuPG^q-(5=nXIhVI&E9BIbH~r8h z&2b&E7kx^nb#woTmND2`!#%&P0l=|sYN_6T_`{dNwm4eN?joOY?VaL&#nqCKWVzdD z6QStRRb@H3u#9`4xWsZsAMU>QR?;_)C)3iZVn5G`sHWL$rO7q(k!K7)xG4@(AYzE@qTdN8b&+CGQ9f@sNRPJDEo=|Id``UR+)f@;NIdnun?4Y^ zuHpe?pCf>SyEzfTO=ObUZchbXj-Tg@@o@5-iBcYIO!yCX`|w2rj+5l3No9a)R~8T^UMH0iC;_uD7bvQB(bBI?sQC5 zMq8-93q_Jn5hIH@>IamM)oeYb6I7L7GSM23CG30C$Y3i|&O0itC^ezocpr3^cRT*{I|Ji;m!d(~R3G_{oZG5FSVx{L9czCgX`{z9$0%N6s1_ zB#T)8`=8Bn;9|=y;Y$afz|-4k2ZSOP7yiT390D`!A4tq^wsMDy*=?u*!#4rePJOPx zC?Fs`)m;YDLb%FrsLXnhW8|c)vmtr>2A=Wfr;g!TRA=8#1tfyCz%LA6y97lUTL(gk z>@z~bx-sayV>uHLGb(lL;gRkNulO`NUoPjF&ob-)cJCZMBLo|dR!w?|`;Hs0 zj|KKHI8@V}pZu&u2|Gw5!rjaM5F&O55bt=*Y60@0?3>8 zu_Msl+iZt@2tRwDVzgei4wq82p8c+~k=Qmg>G>X?VYtznk%`@WhNA zN=p`_RnlClMr%7!iGVN|aPV_K95?P$Gcg{9j?Yf5v?zS3Kot}u#l}}jW?SYY3xAf3 zw1Q7bePQ-xXA_HoKWzCn@$QAf>_mq!_648UeNM=5RlcPlSG5DI>$17i$Q5`&(gL<6 zwyVEK@cj{pDty6pG42_L^l8eo?P@n}3<~-#T+DpKy#28J-jdNzc+kP>&Y5@dx!`MX zz}sNPYbwaQah`$B656XYYtAWQNz{BJglATAGr=dmjVA?`R#hFt;5tb{QcFK z^t59M(anYHGD%f&ZqCZe2j=4*w@={uwwB2kWX&X`w^SIw6n)Y+5fb2-p8%@`O6)X#*v)XKpmRw(ix5EtuXh(XHP;8C zt~^h*>ngWl$>$JN37h?->I~S0G9SrV#24HWrY~Pg9Q%KjI5YQRsJ)$&ZZ67LRLmHW zcFuEVDke!~y|%qvv@^V_5DkDS8J5ItUsHck5~ZyL*x3Cn#^pE#ZQgb(AUR$mGrbt0 z?ayU3SHdEH!$H=ysZ|@a=v70p+z`XVkadxC5FCnvH~(I0C7|6jCzP1RwEh|wd&?Uz z&KfOy3hOSx5O^FTuq=ku%(uoBs939*#GU<4&~a3*&K-i3Hd0KnR8Nr|Tmd9@w%Jvi z$`JH!b3s|v6}^X}pl6`ldCm41;j)0gq?s&B*zDldCu{KV!?D zhy6~b7mwP8yBtUtVYefM;j9lPT1`N|PP?t$9+kkRq1v!1W8N52d^A2xTfE)8Z(P7H zKt%3R_fU+$L8oaRV33{_>F<;6_Ha}?+ny)0J%ER!zv>3DWSx75NggIP0%S2wg2yjA%}5BJr?;ukj{l`6TP z8%BpT$R{5f0cnz2aD{Rk#v?A{Y*tfJpmTKz%vX0sR?XQrGbNVzta^`~+~y^(zp;#X z#j599MG@HEn?Z-RMLS>TRzTF~ICtrWa0555i9S4J##kzFJ=LLx)VBOFx;FXfG0-9U zk_?}2+u=(&q$WDzD6Kb4qaC;rDu2tJo^qJ{ppL1?JUbR!mlL zjNu+I&eywwMtQ&n#0ykrW0B^b*O&rKysfV{C|H{DYA z{(8AeoUBmtn0H0)j#I3A-!B0|53El6^G6Zl>$hiVO7amt12;)A0cGm2h=!V!Z?@p6;JCsji6_eCk>l@j@)J^ zoACuf>xtI27WJhm_#Q?NTBOxE3VWr!g4dB=S08NmE`ayWji|teV#j3%osgXwC`kSg z&H*&(oUoeXyjpi*Du9bFL`pc=331 zIJZXWPf6I?$n=xg_0S4KRQoONl1ZYC1m6-x zelSSy>d=I|Ym~2`_yruA)G~+dGg;mMM19M$!{}i?9L(6FY7p^b`}xC%$J5OSb7e4e zI5`4`QH|Aogs*W%(`lKR@=^`!>UmR9o~98)UBcW-8o~U~cHI2w9&*w~StfSG&Tf9R z_oN{rB|#uta@x@&aQj6fQLFB<+}}*hkNC*w%u=RLAl0)w zCR=Knf^>Qd?&#)bj$xnwx`<~n?v0)4GA=1tG?h)53Q9wQWr$axrYGEwC+{p3R<`%y z5M4+6QYw6Y5n)J%62#SRm@b}=^hSV}LKqEnNx2{WpWOz6Lj zpD>LemqZ$@3*4JV?c$P4zR0h%xhAp&R~z?RMi?{bcwEm>wAC*|W+tj>SywaumHIa5 z9En?Obm%dSiW)%{Bc-D!4Qv(&t_L@Iuc78eMk%4D5qvx=odhfqt6CoYUl^AJYtMS8 zTPOPzXWsp$#Tqr2CgfRUHx-?rfL>(e9+@nOM!2~84?nZetUrwZhO9F*e3?I*5xI9bF1s8xCzm_i)YL7^$czooq$M}S+ z+}hOAKwO-zx=^J>H|$LcT(Sn}vLzRu=7|O&>C!i%Z$lWaO~-;>uQ*j|mnS6D{Bq1{ z*)vCA)zmmz71%ZSZ7Imjih)em^{DR|Ne!Dx%TYSccP*EElasRA+a z(6d0Hc`>0G>=Eu8?@*-3`U&ag2jkld$$4?zu0VG_HQEuSSn(v#Qdfb7YY<8a16SkPv3IBJ ziapcGB9H)rD%XNo+48Pan51RWqF=#Xzr59qs%8Q_M8_jfQ!gb`ww-YBw3^u-o>va-EFI z1yZ|!)aGJq;v<;$MrEHBCP5AYw#UM#Ld#i!A)6g7rChFC!67H#DUQAAOZ2)<0dcAd z;klm{xKkdT7x|RL#uO>KY6}14M`NyK4O`&)dGV&&W6b-<_?GwUDq8$>*dG0-Sx3q_a5Ef-hWXC;e5ap@>E;B;Z z5aj-sB4E=Hq+F%r=J87e0o^lBHw-NhL}~U#1ru5L5PO~pR~ko#POYhv9bK3c2v4p8 z_p<7Mx);I%BCn$B>R5uBz=F)ID)VllYGV%*xo%sfL31!68B-~8qtdMvvN4>*uM*8!|u-6eLNIxV#}c8Jf1@FAph+jtd93ss!_pk?V7 zS{vOc&smamv}JB#sJIld<705FZMf7+X(9s&mO`EF+iDq=d^uq45(tQrYrquDd<2Vi zq`&1+XYsjmUu?Y;7eO2PzPBvKjtui;xJJB69{09vY>ltnNKZcTah%H(m*^U zUg0HHr&rlbvzLYtF0mI7Cvf{YSMjX`YCe1~}>tV+P(c z|5dii20Er;CrvPc>(xJJm^7?fT*dG=LlNN*KgHi|jy`{s#pKihKctxf4Qv*ap>qC* zo)Y&>|Hh<2Ms7wY9ttWx*ikvRnN+8Hl7zF6QAHNuMtXE?GVIn*uzEr644#e?9WH?0 zyZAjDMeJ~|wT<+=L*Q>^NW);hS>+sfNP{5uLf7Vq>qb@R>?M7-f*JJ+m5W*AQ*b?L z6EK>mQbFp1uVp;=_tr$iw2IgOVQ$dfEt&h&m)|nl+8^=HQn@$9C+ohF-I(AT-is7(oB2%X-Nu!hc-eZOo=fiOxu7meh z3$gviqx?5EH&23g1T^8X7(5&+#Da&M;TL8L7J|4Q>j=95=!jjNmU-*6{!)x!X~DvmFWG$gzXmS>2Y>AXOoyy{(51OzKOL65Tz@F&H$ zRM@xw+bHQS+IZ2Jd(@kun7Z1)=z0)zW(;Ou;lH1Qw+BGq#zW)bME#HKKlhB%s)n<5 zelE7e4DRAR$RF3PwrHnITNN2XJZFXXk6>HO#!MM;9mc%UhU$<`1_SdJX<>u!lW#0_ zlrH5B#jc@L4llmfo>eierFVyw*v75OmaIR)%hf7LF^UWyMOk2P4bmhsO-f^iL-8u* za{SSTi;|SiXMXeuL7^f}ZO+e!x%oo^^vMA`q?_$bi^>Z9lgf0`6HTWb?|n|jPa5WO zx(q|l0@P3jdekGCfQAn<04uPu9=YgpN+yvt5FhtYgwCIUPqW%>Cjk^RXdm!vb)IS# zB#wQUkY!9!*&Sh}RVi|C)gw4ws1A|cjbLoG-MX-MTB-?|L}VFSb<+H+W38f;575VD z)6(QOd%{E_IzxOSVkrT53mW0-IHC#klw>XdCSc+VAN{2@VVr};>Cmf4>}G0$%Z?_H z;Zd=qYQC749hU-t?Q`2VSF@{$y%{bOdg_4s#$KrR8fW*Kfo>1j;d~x#G#}^6F_OYK z7Wz_`8WZN77L$d$T|k1*kCg?hnNkYTWNPCzKdp6oyYKY3&x=~-)kioc^G4ZT{4l}> z&(q+q?!L_(&NC(vQNF+O;#_t~<1XJldl!3?Z1^ZU*}92uHdiP0bOO57+evRc8Pe#@ zM7A}G)`D=dm=@xuf=lpNxHQwJjMN~DJIu7S>wROjxFfT|EBsq;`fzRT>x5+$Se=)+ zJMLP~Cs37ILHy3d+kiWQbVP=%0BH4lq4F^81Pv)#_?WLnnZsE?dQ(&$bWXl>r|E*x z&IrERtn@Szzo3+;cYCirgpp@8mD^NTYG7-{Y%tb3FCZ=>VAV}&D_C`+^rb&>nCcXC zH)`FQv}~EN1kmqZ8W4D{bT0N;cR#OGj-i>)v3J@MxnPh7knPlCwdB}}&UgB1R&d`X z1jo}N9lS1H>2a5zSHX|&XwB;knc9C<*@?agF;%;f4iD>c34i)qSbWiJaBS;3V4Hok zP{WMXgFDC6gwO5HzvsHUjJ9g2k+BEj2YFC5UTkTSNmKIg9_ox-CwrZ^)h)wJf1W!KS=k>gP zzyIKV<(>Q7*LBXh&Q&8+9GlNy-Q>Ng>_jG#X&x9B??HUz*c=_u7Qh^HGdv=$;y_RT z`%(i*3&xx{P~SLVYbnytJ`cNgrY%JR_Lo6~PD`&%h(ImY1=!(06xjfRn5N)k+v9DK zg=QC*g=UZ3qUw|c%RI3$j2qZ6>-9`!dwiIXt-}*Y!>CynEgwxhe-|ta@PW0lHqvx%)xn85jX6`mkHQ3lP zsQu5`AMRhdNxZksM0^{W@<*fpXBRR&OG;w%nni8?;AnSx2FObdo_=-4;e4~TSJm}< zF?i5IyYp`4SF>0F_kN5#c^~K-`R47V;`4ZgyScwmJy>Irai!@dia&*WJ*U;%%KLC# zs$yh5@btYSc5C8urUeE1liy~iZCcCG>pbB^fBliE~lbeWyedxXFk&*-N zzVlh|(y)vD@$hzjQKnw?^bI^%MSPt4vrll!i&CYKsMc9*Vz#|Py`36%2E!uP4PKR4 z?zm-hmB*==)x7LK7pl6@sfv_G<@Wryrngy#t}K#Ef5)#} z?>TPGKQo8p77r`xd$?eRIxv6u51eu(Z{SV8qQ0yaRrgXaVoCc;$1Pbo8H?5~1YaN8 z=#jSqWajy)?`ZOruXQ30f^1zgA;hh66i{0I`NOwSVa}>f^?!_CwfUjT?r>9h4&7KT z#KanNy^s{}SyinQo8@rdebJ*9G*x98xM(eqeYp_GJrew1y4IIODKF$Qc7@LF+AF4% z7ucwTb?W9Z&9>*WsgvdCJ$jAO&~ft92%ZC~c2@n?4;#=Qe)kM!3N?1R1nuq-5aLbc zfO#-+7tHT`E#4rnIYF4Anp->1-ZI24>`GDYA-Lv)DR!svev26p7{-HUr&ymHHsZ@w zrLb(Lhz!0^g=^SV5+JjroeTd}czmf1W|!@jqVL?W|BbaP%%6KRTz(yi7-hW3^RcT? zzW)SyGBY*FefWOEAb}h!-2Z;=3Tha520pTi7>IPKZtpp7m)F_0aqrym7mht+@tdio zOgP4DsGs?uD;?O0bv~ru6=AhK^^fu0zusB?`ipUf6RaOdx|zld_q^Kb`{6;Qpls); zD}Ot|AM~}$fkgW#_5ydLq&OpIGq)pmx+fX5`GPj&v!P;KFcm7;htb* zLxGrJ@&QJFkqMnC|69`*izP&IZ&7)&FS(RtTfU#h7yq5Qd(VRKo>0htlKYhKwkqa- z5|}^rR!?C->Yd36kw3SxEM5xD=PEb&Oqg9)LZg48)^Li8e!jt%qgvYmgoJaNChdDkd%IAf19^`fK|3YefqrY^@ALIy%Q8V(*UR$c$>}t?v(@v3M zhc%#BlKQ(C!JjIR1I`LnsPheIdtEX-_<~IvejTm4yj9YF^p4E`=GZAK->MKhZu-v? zZZn-OhFdeqN!MaCWz6PPUD>tp%1r$Y%C_7Ir3p27(I^|uVaiD^z@!fLNwG_0wo5uQ zevq8={5k7MT3feRmJcH2{+E-p`Wi+t;4O}ahS#r{VgR(oU{S>@##Sw^z5TUU^IAWz zZXO+X%R`=%M?fVaZK56)5&~0n=2OA@E3UxY)6IkBcDVpV07Mw^TiD~`ASu|+X*^E` zPpau)Bi*`A+H<4L$yD|E%*j*jEifRxmf;|KTp+ouz(>6jYFNuqkV#dm)yQC3a@$9j zXc@Ru(jg!bv_*fY*!}a_saqw51o^h2MEkLAGAN@%iM+q`jparLxgCHQMk1lu94$3Hd=Rt+__-)2p$&qbeb*eza|~dk*u+#r$Qc^lmSrML8C!v0vc7kexr%zm>C> zcj}&17d!buab`~R&L`;I^b4y{&O^sETb6aK&xiOt+Qsx0Ncd~8O=Tyb1gi71H1;?J zv~EJC!HOEA@DWS`KZdb-t~{xX`x10X^4O%MOEf$H$HkF@3I?NtW9B84Ngf$#M}$&LudZ+(xvhg?VkY9zV`pP2n1>?C|v4 zl6t)(?_DwTitcETFkbu*4+mShoWk2ih9ogb*g%q#*VPeIP#cj!M|1O)$ujYW(x=NT z7*##FZWO0EDSZjk#PO)q0PQlLJ}uqV)BdZCfR=a3S$K?;Cx2ZN?O_>^W#`N#)y1r8 z-A!0BS{2Qb{Kd;0jNMmmm5l(;(z}K@KuF9zET2bz{Uv|1?;SO-&N8LKu_xpcFYt8P z`!FbKM=YsBw3_4|mSCp9c8l-8#WvBfvT)a5r0-}3Lf9c}OA4j)q-iakn1Dj=iKaw-4_<;JYmRp{FTvBPX`QRtr(8aD1m@83Dq{i0`_H6MYg7r1L&j-|;$ z#PjFgWYh5E4u46uzN@useCwL{Me90Ip5Kfvpn?ej6U^z-pY1a`#!>9X&;ruXfZrGT z1BGc9r+;3<>S__g_0p7}=1(DPOAw)P)$RDXX8Wb?C^hRL-ZjyF{zged3(cEX3RsW&X6|tFky)bx zAxVCe4e4XjIz5}vzi>5}v68eh^3#uW(YR3t8eQNo^cb*@i6+{McGR;c4)dF>+0n7`-;~4%)ddb5fh~mBot;IQx!0YS$jWw&FUs~XxYOjUn8<)WGsXNr zX4a-3V*Aazf^SVtResuv0;^pc98+yYJKoV%JXm~}Tb-P`kc3?= zK3(TC2l5+7dVa(d1A2sk%-8QD065uIV-p9<+-E zVD_vg#mXp&qz(C z6~&B#`>&0MjuHvrE`$r>i0S;(F;&`!Ka%)=3t81xuu`$7ii_35llPq!jFQYgoN5siIrLa*TBxk!YuL+NKI@*&%b+otdX^gzADeZ_X6u#}D+oF{2dCkzy8iWqh zKIJ|cF;iV}>IY;}&P?0PF9?rEs7dGfbh&g_W;-m%qS?u*1RUL)Z+S>dYvvT1o9*%A zxpXKPQ`1w7opx8e?+EDI?UGnzO2ed1djEYU1chWnO0&aD29J90!#19}RK_0ewOFE& zj@fR11!;c3XVu9nXdHHG90W-Ae;wlVJdzg}fst?yk>cVB)$p|q^^&&s{{molVrJ-X zb;%-cz_Uv$pRFeOg%#PLR^7V;s+5Xr@LlnqI(dPDUJ@79l9Oi*3`x>9znD-Dz0a|i z9sXOT{mtoTSQ43WRGhoSQMf_#{s;c3R{GVZH@lQIIc3bT3soaZ*sL_g9;?mm16f-6Pf?MeT zW&xD9WR{tRGkI+`F(3 z?yI7%(}5Tt6SA0pVhQ43NrlngKgy0^_q9lGqxjWe4q$ujgSsekDNyp%1tYz2kqt|m zz^~iQPp*sY_jf5XC~CTvFZBI>t)qWl%XSgx9E(7jJ#_~{JIP2B*LS*`Z!bgc$(#Gx z56TJxN8c#ke)J>5CpGOd^G*{WG+uGK$Y5qW&Ipllq)+7Eo>(X7i4bU-D-Nc}?HQq3 z9oF@xcfe&@%|ZoIJVNE zaJd^1YPOZ+QsKfKX{s5Yyr6t*Z+?N6SLa(^bk^qx|Cpv|6G%P^#IzYdM^ ztlodziKJZU_P{WcyG(dM=AX{;G}u!pN_Jhy3pk@1FM|&pJsJV{5CkWEKEl&Z4V`sF z7!=p6wPwtCR>6@3T)4tx7Zi--HU6OerW$=a(D(KJo=}4i8B0jU`4=SAw=8Ng^$D<3r->H;XW7?QL z&U%}EZ-XmL&a`Q43RzDyAY}U(iWNjtn%{p_>`^)5zuN?WO6qUpZT8x*1;cBTu6oy* z1(t!2qsKKrlX6KXGnEZay)TE?n@Yj&9C?$$dx%VPg9|U8}AbL{Y|6Y92=YBc8{E>{+ z2@t}uxVRiA<4)v%(VAk1GZQ*%4wk9UYAyG6)HHJ{E=P+5viPFFl0?n#M3XdFA8%jU zJ8eufwHjX}zO1zb?j#TTwq@7y)Y1kHA&c%Vmt81*hl(^Jw{4ykZsYBF)V$)6^0Qt=YMEb!2G(B?(zuw*X|r6yd%S%PwINy|4)$Odors+by` z&d1HF9i@cJ4(3atU7soS0R^-+yG?ARW)3>mS}VMtUdvgSkO{|@3Pfj4e=XyZUlRRi zi$0fHk}PErV-y_O%dTMC-?0O!#s4;qT$NIszf*Dv;D@%nuhCSEZm|n5lgl4%6SpKX0pWT57rL8z|9q~1l2u-sAcW{qK~|@t}N;KX|E+6o9XoA zVGHhF)ps#EQm>zR+;lcxLj~6B0WH#3yY!WB>fr`-FoT8Ux81|Ti3W)^xQMv9PC+5R z=<|p=ut{l1|5f)ZRTnS;onH>MK%{uGG?=LOw|^&7zCd(6^UO6TR*5UHlu5M|Yu3(f zq=RKAfkl0;#b+k z2tLu|=g4Au`OZ6#vciVlR2Fx5h}76v+52!H0oKf-Qm z7(LmcABei6Vw>(^h;Mzy8b2w8T8wUu__*)I?YMdq@OnD~3dUO>Ol@?1;F1c73^c3A z1RpE=WdQ8>gU#xUN70=W3J_y4>q@+ir$b2)3kgS) z4Rrd0t3At9l!WB#@>8q)}-qC#8l3k*6_lA|@Ei5Z7=7O)${w^b_?fBgvs1n<*$#%Pa9y z+|Fchl0-}5EWd{XBFE&Q+?M6p>y}N`f9J0>KXk{=b(?BY*IHsq-8TtN$zjFh))d1x z9(%Z9mHkJE!(Ubml-5z7?Bv!WOZxm!G5${eMuO316$vxAp2k@UM-TW-eh^|b?w=U{ zkn3zd@I?e=rZ1Q_+e%maWtmh&8fjj@J6bbswtIKwdPAY<4leTaJ7z9p$1U$FVf{g@ zz$eq;=`En~&{j5t=(4FwVV$?KfL%A1MK~3RhPUV!7_4*f?#ziaP<(>?0u(9MVLro5 zdfk{9>aXORf=RB3yL#G028>^SMy|?L{#wm-Vz8)BvfkD?KmgH?O9VxoUF1xQ9SxdSCc)2 zLTZ^WbCwTEkjF6{$3cAjV+v20P(CczR6+Zw#@i+^bCbSa~pG!b!b&sen+^K z9pkSLughwQn^$asYnMq$$|j(Z`cpYSu_o>#>Gg`qB)FQ+>uSVwo=^UQ)?nRgUg+(7a7- zFVQhj zE4AHH{+`*^Sp%s&alQPDJ@1f_2rJEA7#^X-i(D+G7pKXF9T1$-oVJYS{cB#8YXRH; zUr5gzlPYNcrUSoo*kF|u{!B_80&Ra;9?-W2ZK<_kq^1fIkJ{UDE(>;1aTmzvwbGgoEQ1T5Z|P93#)W| zo7EPII8dmaACG8~?kd7l#vB@|dRWy3rYYPfdY37NIz638&;=9lB zC^I_{rt9Wg6~r%qRc@=vjSp> zk5eA|j(`#UvN07RiW1kqc#=M~SK|s}A6GNCS@7+I-d5Jm=phMMJ`psgrN9Qt#;^{~|Rv5g{96nbBn50#+mMY{szR?~Vhctz}}WeG-)rgwd=tOw8)hSzR+6om7vk)ufn$E zL-)FO`zU+BzKBk9u^$Z$A;7M96*p<`LT-;h==uxG+e)!Y3ttKr*C!KTsmw(vuv>%i@PiJJaQKOZ`43A5L$EqK zZI0?>Sme>`V8AI4ge1Y)i!pZt!S{5Kaxq@>exo!i!I#NkP_O5@T&Cb9Zd8i#8~`wRbKIP&SSUoTWF%R@Vy7 ze&i$tuGR18_dWJArOnke-4wyQeZxmkQJ&56p*Q*B25{MvlsFwdM*Zb)3O#m8+SC93 zPKoj`Rm?|=+yj2g7#!E$G`t)%Bl6&RyhXOJ@ntgOiq)-s|2l1TXGV|AgUbNNI75Gy zUNVVq!pDOmfD?4Nq9__M0<-YyC`Wp~&!3Y}naX<){_YxV;Gq491B|h`Hgfnt=Co9G zFx?aqHZl4wDM>GooRZ}4e0`&{u2bdcHGevH<#e#CrNJfsIO-cPs=w4?LZ3{y>`572lLaX9+` zL5TJBa4Nvjnr6=Q<48Q7iZ74v^Bc-A$=_lchGfm7L?d>LiD*Mfx+q$KqQFJ`XI#3^ zf5RB0pO0{N_J;#D;cY%Wpzm3>=?PiFAhmjXKtK+!5hyC}K$oln z7eCuzgZR0T!tk!`uAih2KWmOteboOP>!_IU;@A zXqGRjogzsK%+X`)_jIAgcY8RD(~v4wm!!_8O2LZplQiyKb7lpD|AWp2;2=ewGy(Wy zf@#`(Z5-*(szurt*Tom?5BN#vs2Xz9bwNSc0SWjQr7xhlQd2kEO*K6uQD&sHlmsDJ z3%|=mmg=if%0Z6y)}3n2+tq0uMK|=s1;wbq6Tvf_0pc*GKVxve`qh1LSVqs_39cFm z*BC9#;HU`q!3nXA(efgZW%Nd}l82zJaJzR6rg9Znz@v*;hsmKi&f3=UW~qX2E{j z72V~epuE*EQZPJjzQ(6+3C&GGE502AwFv5mK~Sw5o19uw0!k=&KMWserXWT!of}Mp zaCBkFh;fmy&XcM#BQP34umdU7Yi^H}n34S`1B~O3$7526OdE|#0R58WN3`I3Wlq<(!myzQOPz3|U)x&{~mWkE;N{vXE$BeR3zo*>t zAQP+cT`2knm(p#}nQ!3%>}lOC1<-)CtMB~ThJmEe45@hq5wRkA%*STXbcDXg)?!i0 zO|JGtSdL9aPbOJhbzn^SOde)L6Q8u-UZRU zL^}svOJAaXUFW!5wS9qWiZX-4RUJip#F$PU9J1F5M5EWERDopl{Lu!CG>G^Cj5HhC z4FI#b{h}%UiMy$TF(#&?Ivpd*I{WqSfLI^XsXp2LDxqmMf%<1`OxVodzDd^N#*Twe z)YY2=W9y%dYsZH%7jxc+i=Jw6tIV1DXtoLl){!SE#6y{)R#+nvgcG@Z|5rx7} zhtlUM*fU2tsw{?ixaN)$niEDtJ1C&U#T|>t+L(;5&rcVUAL6mk;@fDKB8oql&J+7R zcwLSa9CI6YGJ~4-+7D98|E}_`eFzh7k_ssj>=%2y*x4g#V5r^~f2S2aqcc=9A~JjS4DR+->U@-4A4>37|1O4M!Jx*DI>2Nk;` zS67}|F!OyA_;cMjfvrT>*cHR?Y_FcARix+jZWayK8<7pZ`6Ie2xA1u*cNZFKa9sY4 z!O{tXeS-UHtNFaV-h_7I%rg-U)Xq5xuxWBy@-^jXpaPh=cptI0dfjaPdOY`L?4<-TKQVja$o6Ins}+x+ysQO8#FgUtT$%r^V%XWBRmarsPP$;|si? zS72Cp_bb4p$04PgP{lu2Jw-)IN;1by-M7rvFaKhiq?r#<>(`L$u10rQaaPT@W7gHw zRiwCTje8IGi5b)A^eA01!J5m?323$D$q~6ULn6;LtJg-9H!d7v4l| zwXpwnrx)2h8PBufUwXR(n`Jprw> ziwPmi;8UWzNBA&1C`>x(xD_C(#TeKfGT9~nz-b!0#28_aEk=O78tzvr1LT+H-v8Ok z827rGf!?QrO?>vJ6L=~==>G;R5B%6DAL1w*4*?JOntvO-z{|}2-UM9v{bO=KH5Ik_ z%GJLKrL@g7UBCQCoYwZo%7cX3;|1({>sMmUddf?rNzs>qNL!jSVo;LgMJ%P}rUwi^bz#vO@LhBoy{CL9tW;J=4I#4R)k zenXkTVzv;ty@@ATA*w)VCZA@ku}R@-ym|X_tvaPEluRTe7M-{APOg6ZX zgWlrJrMBXWv({qQONu%=R;IdkE@4oZG@BdcfM@&`Hrv33ORgOW=s+2?gGo-+0d;Y! z2~0xn4_vw6YLAVNr=uGueD-VW^m%aY7VGYvd)Usc5fOtssJ`PCfQ~X_QNN&2dwyBX z0aps)3p1Xl!^KeBmJI_thoFHkWek>?_qB1qDywRp6#T!zr}W%z-348wz97t8!lbhb zHH>I!T>UOqvz!`((WA#S7~)OSNVR}wmp|A(S(%X#F?Rud!Z>m-X4zt_jE5{ONxLv; z#x~p|3YU>uoatpl{4~&rdU1&6WHVQ1c9(-|XsE>aEJhjU^TxalBQP0yLV+%5 zON*OmRU9sw>~u3zVQg_!RqXB&TA@xx>h>Yc-*l{m`}UD5j?wi=!M+ThmLET61y;Xc`j<6qjNR!Ggv57Zkno z1!u42<6-<(mfw8QaHcdFD?QN;?Hgs&axkt;@jo|fxo(9g1Czkc=JCAjg_*rI*!6a$ z(1jH$i#(fch8t_s&{c?(pqvSX^fmB?L>K-&cV-UxY3m2b*4({*I_{E=@S()JF_J#h zf>2&FqSrcFo5ue`H)AKBO)nG2isz%go>-V8$`!j~XZ}4bIy|(_P$g$^A^cayuK`&1 zFwN5d_~W2JaJu~eLEI*d`)+}f)dsJCB+5SnTj{|{Ja5|iJ$WOWU3lJG0Wbb4 zRHgj3gSx(NxsHx!X6_F_W}RoTl`o+Qd%`t|nt5CQy` zy4ntmwb@$y12ejRV-qa#N3M$2Tw#oOA1_a8jk61S@GOlrHeW1m7k~iILrVo_Ue$ zneGptRYwzPc3LxWCOMe%FUEc0oeo|TF$#tiKRo?J(BKxJ8@g61gPBWA^J$uw?$Xt^ zFW^b2OOWVl{2;R_e|E%S5;$Y)5?IpEs3rEaj^v-kf6kpJXSiHc`twn;1Nf;+&$yc~ zT)t;79=PGU(I6jR^XxLXF);;LyxF&_rTV4SW(0rjN5b zpIw`ywGC=g70q+|_42|-YU8Vj!3$=WuU}Q`Tq2)3D42dvPB&oC@c2;Su3~~6gH)AS zACV?B0nSm_5}5CC2X8(U>TTN1^kLoVNR+Ah)H2Z6QMyFMw}u+9@AsS$-u=cn@kxSf zkIC-ACqHBmbtASTF};~f{E%?YUeWhwUy1aTvH5ip^~J-;;z0PNv1p!)_xG@-IZa0r z&UE7w4gPQSL8%)&NQFJhgL9u=ztFm-HD2jxP<#Ny!6^uS914<@1AQ)I2#%Gq?yXV7 zgm1j^i%_w+8|1s7<9)H7{!6N}qAs`@JC~B;)mlj=#!9)}9A}FQ>vjm>$$1O4N{V0* zFW1FUlc$;mgAVn083;9%(hAuH6UZTk#-bgrH??!6NBav^i{CfiV21U95J zd*Jr_LkY48GgyQuyzDG=Uu>T~Z*hPX33814>0|`|94CHRSjP)}!>gzqs0KDn+u}P! znU=ds!!LeTVQa6RmZ2K8t7pMY6S-J*1HnZquG#O_QzeZ!A2}|IQmIWq zwl-WLJ`hHRuRUa17e{{_`L0?sQs2-dyceI5OQ34u$;c z?|?n!xSJT02ARcn9rLZcwPZ|5PjQHNy4-`=M|iSPYEf}< zB4wPo580X&pTfs z&Vo3`K{gD*`>O^c)g@S!4L-^OL?1&FbRX@J_SHhuAJGqJ%y`t_!O3XasmH-j;Cu@p z;Eah45a62AZ*?YUL}X5j*gje2%ckW8Ekp>v>-t`$Pd3X0S7L4Y}TN z66=Vkg;j3om|E6r7C04pyXTps-H=J}l5}4zj`=ww`?7Ffueu8B8vm}RFrzXWnPu?R zb5gs{7Bb%(e7yE9d+6P6k$_HEE%U};mhOumG`4EksfF>!8Z};^c$5!o-M}kbBnMeC z6LA(70Je9cXb64Lp$2?DeaP>gecql6VZwhqM2_?h1*V=|roEeQ|LxU6oXhXqfj347M0y7E8l?ySx) zpU~KapFh0z6rjPMrq(W4>e$7YfXx{8pXqubTeCLTSYbxxx$kyjh zAaubQ+Le~9)lpS=shKan*KqE$2K#WPK07=ra*(>aqS|lN9&L~9{F<<&-evio!N9B8 z118^e+m`&(c)w2_=LDx&Yn!~$9IPM;r!E!62-kl7{9~C$ZD{}1Q-wLxhs{@S(q-nQ z(dLerBCD3BrNL|M87m!y`jDdW9;Z#}J#rUL{q_sj3TT(~!P|7a8D~WSVRO08SE7qg zr#qc^qhE`f$E|YSOvl5b?;mVnE2K<(C}D@*%LheKMlW4603#n$8E@F@p1Wm}5*fUt zN?;d^R9yghrgOT4Jv#Q4P!0>-h{!D)ea9#*#zXZ;+#D)00aShPLea;j`w->XU$>SDL4sKWd51O3`OCer?*Z@Bhi-EU_WoOkL&v!fPQI*;0uXDBMFYI83 zkOA=WKAu``zip@!5e`0$zex0e(Q#Hf8Pq8c(FoD zw*NDrVq`GtY#iz{!)C*nw@!i0befgLMtRO8?eS@kEAMFw`l6dpxgtG`h|Y1%smEFW z^g4z5 zrx((PCANms$i>K`BmOvPNm|N{!{GyQn-Ud3cgkoE?O~12O%|sjx1Hay9_W6g4)FI= zy0-F}N`@zU_47H2!8;3YeiVcGZcQ^$Q%Uofq>DIJgVAIovXJ@3HJ|aq3A@juL8wN( z=FKOVtWpMd06dO%>q!J`jy!PR?>$zP-7|O)cvPfe54M#$uPg>*Pc~q2VP=LMs+(XU z710)DK6%_cB7dg+;hks2?B0wbXZi!#n0Rc$#MTWjcBRXdR7ZT%OW)vyLMvXz z_6`^9`=_GjlBflqFUUXu)~)~;N>C9JM|{C08(Cs2z2=z5i%B-(LQ%-b*D_cvI_~+N zT!Rqw$JZH`LOC8kdALC4x)W$C_0n)j)qB?nbN=~8@-7=IBEyf{8CNwi+SDP7>iTe< ze94gxBd-9J($ryt8gt`${JC$exhWPpv@BZ6e&d4gNS}?=`(oc_K7|=}GPpPeFwFWh z-vM^dmvjcajlqD|MTyxs$?>*-A}cqHak`Nt{%8bfjBUFz>UepgBleYPztdk1u9R;U z()N3f$eFNKX&LJu@7yD87*-x!rE)^Y{FELdP&t%$ZMGO*crdi&^k8jIK+@veT;qS& z#<-vk{Ae&N$aaL#)Zy}fjM5=r;M#}FXhf5h>O89Cy1w(%so8~Zimi07Sx`Pu#_E40(goKQFhcku!*i57V=nlp^BuYMZ6?HB@^0=3 zOSDV6`pTOH{_u5}9#|%?frV6jnVy;|kcac_RT~yGXy4R=K&+i{wtoNrW~+o@xoY{l zpFeFFb}5s%?}@+{-U=$WYJeJuhr-O}6Cl!zzenvYCw<8TIjzXwC1qogxCR^1e~+ru z@lI``9C;M!UFM{R48Aw@PB_lKfuH@v?r3onkB@57w*C#|60tNi9HDtJ72?Yi4eu~^ zQyk^C5b37swfojcVZx8+^Q}(%KJFBclK_+$sw-bD-+zZh@M?k_;5vD940?a3E1Q>= z+0E;N1>Lb0iT$$ud7-(=?vbVlSvSeoLyGLd?L-||@)Wfdy(Jd6N)$O3B&2U^^RyFg zM(E#m`+f7Dd*3(cwpqqk(9khc?;|1;wO^Dt?ItN@){Enhj+N9Z@E${)ePV2-B@?74 zII72vK25rc92 zU_C-zz9A0N0=%R}{N6z72?8<~h=t2>4q}HcYVgwVi3m~~yjP`TTH=zfRl`g5CEvw1 z7^i~^{$V*=j)8JaJHk_XGbgl z@!)oWMJM8zxihK)h+6B9kD-@YQo4r;^zMPM&<&%S+NI$&o;8Av4SH|9Ev4@S{j~5V z^=Q|7E8FRRajW0Nb&mVKsMh&6#j(@5$=#b3jLet>>JFKPYveCnRerPk+eQ&BFJP1C z2xlp%wDn`T8%)ad8MB=}9Cm8JJL@q%8I*;*wCIB4%|RZ@))cTGKFF5yy9fR(&a#HR zO<|l<{r!&3Cka;v=9P&0bXshrkOYve)u4)rPj~zuRJn-q!Gm4>{hIz7{Z|a(Lhuwv znxbSQ#B3R#cEyT7)JgW+*pSq}Pxiq8*hrP|?g~ueF}&UaJ~-1=d#Q1KHm$>S@$ibg zYKa@)!{hfs;sVwq(kp*rUp|~?O-cHMVz?9<2xEPua?gBF$=qCA2M+ghwQ>ej-@0%0 z@wu!S1`1I4P^cH|CyM+?1~oa2fYcmy%WkRo`7;LvC6?5(2$N+FBEP0FR(ma^5LOXG zADs`f-OD2_Tp#cAXIlEGUC1`xexD(p*%eLBa2QauPFhFX-dN?!)N7hGW1gI($0Z9I zi`{uA1z5BVdD8r%fzF*eom#jP4aNE&{DX51j}HhB+Oy3UQU zg7W|RH4oIUVP9_i8qr>2JW>%6ezp5bcKnpN3Rf=)UM_+^8*k0i`&|HxM@yy!>ep&J ztG_I=?1~0DRBEOKm^%T)hS`I4LfO9G$!*w(M+xa^&~&8Tyn#JZ)-LIUBCDsgjGcpX zVu?S67#^(j-LtTKWAJqqIhHqx$0>IK6AA%3O;7NN4^ogj=1Thzy5;_3Vz>E z<9J*AYYwxLZBr}b*kv*x+;}_JaGY>$>+gvweYsDs#dCrmEyKnCQYGL)#&_AU4X@amuz&jgQ-Mn<_f}gpy) zj7*oXcsVxI;V0Dddw?*0f6gVa983Hc+OGLnNiLS+2oM=}zo zsO|VbkLeezN?4_3*x%#VxetR!(HuTjaQCePGd*%cVohUu`Ol^(=Q_7joq9|85L1oL zJs+5k2RFa5+@m(@mR=~K|JOuP#8-{-qiyAq!=%k@-Mmwg&*Q|-7Qaq8=@_3)|{_n2yS=O?69f+V#lwuqIaadrLDY#`CRSxOdE@0(1!~ zk!QATxD#bbV z?C7QIsVCNRT=F^Gixoq?iIcj~d@_SP(qP0HsLlEmkR9<_61owf*Zhv~*%?2Gr`Ju# z80Yk7#f{T>!qp>(2mpIPcU+u5+C6r5iwK=WPqhI{Au!IHYY&!Ttb`JQE&AT_2+v(UQP;HR`{>pgjF*p&z zXeW-Qa9tD=_OjCO8!f^lIVJFSXA*MEKU^7XVvAIRP;tY)rn~RhZWo{y zWC~%rPP~nl5Ka4O4M;!Sc`C5p{9P7XrM_2^J9OVolCavnWyC+}$?+T)60=mM1$eNMix^q?!rZoU%wWV6uV zzpB-R)VS#uYb?P^2;B^JkYVG1ckkk#RWy8Rg`Koz-Gj=G8EhdJvT>`TI`198qZ~_y zfmF-m)7w@V`^wsB(^XSNXHmhptSQE)jC5{)2I3oIIIUwuS%3r>({N1qEon!137@wL z)@9J=2H5&6%#WybMz(6^cXuT+b&cp_E!zM#xeM#ye&#p`^(o(8xKO8=CQHa}16ygb zgz#xpBi=PD@!qqenI@f6GYk4X=hAou>)8n~Qr)kLSis6aO%(XQmtYSkh_!DWuUq z|1xDQ81o#JQ#*X!V6tc|%6!T>JMFV5@q(9&#efnt~~tvUmHD{ z@muj*RkxJQ5~~@>7MjtnM>6uz!9lwmnf#{xr`C9Mzqjw1b3FMB<)nwo(zp9Z-Yay; zzC(p>|A)QzjB2uL*F^;sRJur$CZb@Y3!$STq9~vsy>}4^olp}20V#q=?*a;%w<+HYoDn7!ug(4mEN=}ISq6m8Wdwg4}<>ufem;Ps^&>l(unn? z!Q9;hLx)v$*`pr|UDWrG*%891F$57m78_j;_}LFMtNL(@E@{`2LQ?C1fGm~E=cy|R z9)RC}b0e;qP>GL{I%B8>Y}2g~`+KJV75rzSF|RMx!vvELFL4+cYzKZPcAF?^=(O=y z^|K904>g7m{PXHkJF0sJWE(7_6vVJgmvq4GUqHRkQuskQnA~)LircT-|7iR?7~<1EuogfeW2uT&|V#iW1UNdYv8bgSR*d?3 zR|0KqWB2k19m3FE6Asab1TMwjNA*2Ux}Kx2#j82QtlkSaP$?dNCye)rL#}3w*fc#2 z#nRZb(9a~W_E=f6DD;Wrkh?P^-G+O4X}7S6xP?ns2Odz>^F>RI?UWNryiF<~87`g~ z>;q7Wb9x1Q3oX~_>*L*AjuMYosVB{OGa9NUZ!aUHF*Q@JsUlPhAm=pu66rR*KcZIE z+PW^R#$F{HPVoH&<*{A{JxJ4+G+hRd2*HNWjl`PNhDSIkcDA@_mvZ`yg4^NJDtnXC_X490iri~v2Z-&iwJRAhp?T$C?tX%*Fa{-OKX|v31i!xz0(0%jHm2& zmHmS4sj1tgriT2y^m}q$Xt*g})+vk+8|tSn-4U1GDJ&b>ipJn(CcSxeSt!xBPa{}O zq!H5zOCOx(s%U3gkz@o+Z>FP1;ec{}SdfYl_$2-n6>K9o4`mgr#&SlWhy(*_yUW z?al>9%LO8?j@>pbOY6oxEYGbMT=R^_+in9M&`Qc^bFis-@Cr&7io^xh@6o{k2W4S8 ztk;7N9kE^=dTa4FE8n*$ouAn%V#Z28_InUZJ*kD^W~=n{q?A{G^qm7PAvIt! z0c$zjo;*I>%%2oy0F9L~;$)Q;vBR@hzEzBH>129X)>4Xcs2o`0_TS!rC^yo$lfZ$J zLPy440v`I!q*ME~U|YJK%b&!^6pO@n8xeelzGgJ_=W~rR{ptBTwt%3g-;4eS2nfHh zM&GBd0xV@3#MNc%wb)OJOBz=uX6p$Uo+M>Az!|#EA9v40*oxa;&iP?Pj-E@$3Q9uP zm>b~zgGdyC|W$f^0iz+zmo#WDIU3ZTY?)l za;XQ8qjf82T<`tHuJZwR{m9Ssxf-LvqZ#?;5S*L=+d?%=ojYO!ON}BJn7@C}V(#)enI5VpC5`!u@Vbe33%( zXvmCv2T$WY@Wf#%Cx{Qd1LRPyfy+=-(L$}in*VfCLf4eB(}Baw4e^{yYLAR?-h$iU(iDnO~o*?iJ|6MQ5w@#kzs-V9kkfxVs}@KLc_iPh9~8h+Ef1hKNpf#iu& zkc^~G!nG>uKb>tq&5lPN&iU^4^?bnE&-0l?3Ko`@drF3;d$pB=2|G3AFKdoYKJ!I; z0!$pu#7UmnC45COMbL9@qJuh%Fxh&S^1ND$SsC+f3+)n*7l_{i%1PwV&qg zNYw|W+)MS?C_@fFco87v_4$Nnucm)FI$!@+B@~8_Q9*gmV&cXHI`hK>QtGrC%Y1pf zy?h!WI#xpMDgZ<^cSphb;)u^dgt zpe*JoWP2ab>y(v{j%P6yM=zDmHf`}q?bK{gct$flZpGflB&i%$Lwh3?yo*E~=1 zGsP5(@IRV=dyiW{mY7T4AF)b`6~`Xdud(tlQH>Y`lgSpN`6y6c+h}8 zPDN!$M2^{+y;;BG-7XXW>TJ|oO%_w&%NPU91MZ=KCsn}R8<*v=c`;XF%q>({x1!ztsVfZg5tx z3})7v$9KA&BAU)q>qHu=DO{UucD9e4vW!Ocst&152V3B0Nk5hl%@6Zl3c>hX4z4aL z`mJkB(qmZ$ov%K*eX-#xnZ15!-bA>Oy?p4&e&j2uJDIGMc}Hf!Ab!PRD}hLC+cf03 zid9{H)~PrHTLO>3Y952s)%B^_BzLB=zkgv$DDk>K7P44r%SK(wT)Q9VUz2bp>3Iw_ zwS;Kv!B=@l+xE^$bf@_~$5FpX+2_$nIm?6->!e$g27Cau(Z$3$d@2_QJpVgQD(1`L zEQpEjwU1V>V~O zDj;K?Rr&0=kQ}k6NRRWJ?;3Xy5=cB@NT<+bAw6%dPr(z|je(IpIyF6YIIxKnx3ltGo zx~Z|9?q|HkZ#esalUeBcT?Vfgu^5>sw0YfQnZ7N=yLuGGuJH?5aOhJweQU2CjuDZu+ zQRS~?Ylc`XXQSphdz1K4f@ievls}0w&TmXMyoZr4Jk^2@8>9yOToC)jk$SKy_v1?{ z#mDsOXV&P$rwd5WK%8KSVzMs`9?;aK5LSC4Spy zB1GxDW$L4XRq=kOU3*Vu^~ifPpjW_O=Ac@=(*4*$iV!I^qt*}9z@H8huOs$S5EJ-VT+G!A{aNxSr#m$0~K#F%;X(K zNVXnv7kTe<6onZETJx|VN0gcLyLN(2q>5C8 z8t}sP<{~!j6=vM#{u;f?uc?ky>M!G6;kB-LwqK6aL$av3FUc`>{<;-teUOycsz;|l z@f%8~=>2J*+x1tS{7rfGZ z>DPpnc^6zLlMv z3W-S7E$c;5wbw^FO@s3+Sv zDAWu;rK}jfqVSobR%+^)C6t0*^ATw6$Wm6|YjiE)*Na$?+T+#hOkVe870tgyNIZG1 zy>$^JTkhauWDU&&enE@v)%p%^xv)34QoK9$B=)*Ea>7jgB6xb=di>?uB=H-K88gy< zk(;e9rqx|bwijHw*qojI``|HH?SS%*>+H3aM~MWEUdw_RsOHv1BDwGUI_URp&$q0? z2sya{3w*kaZOw-*ln56Zwfw@*2ry_dxz}lU+kd8Mv#ZH_ zHF?%h#zun<(f@RK#PK1?84N>4J-da2~5z35qdVkQKdGvt3hk3o%Tsg0Y(=R6ouNxSFv zR(Iv6HoWYq+~GzZs?5AysWEvnI<<08!LWC9O?ypoHD>@%2fggKGnALP4?TdciSaXuiQ}Nw<^)J$?S5+8S-YLXQq*b@1JKijgXkHk-DonYN*|tG#MiO>EJW zei+fbBA8;)9CF9L2sO0YVCfLdEQx$0(uOskxb?2k_4qdgk-Q*i0cS6gQ{Qr`YVBv% zo_RYim$Mr{jW=glg|ibI7+GM?Ra6&+Rk~c=l{1DsTN=MRDG3EuKaUr^NIcEYQcBl7 zqe1f&;rhh3N;$!APNxT<6UPsIvzi-$!DuB%QwH>d^Vh8^`K+|d^S&73P1uFON4=#` z=G9hCC;8K!2m1B8e)DI)4NFpyhGEIj2D>2ZvTRW%uja)rIz76~)@Y78?V{~dF3U$V zbl{a1nI40~oevBJ-6{<-!u?BB%}K)ZKG@=$d2MniRiRM)P-Y0?ARK|Ry$ZyXVXf_* z4T20@I?hsWw*aw=vn`Y7B$+<4emuhur2|#OlTI=P9-aLbwC zqR&_Yj8vUXq!qgFm(_eqquKgog9gG1haQzn`WFG>kXSW+CQ!R=N4yR-;c>aL$10Op z%$lhe?-<4LlR|@l*X%IOS$ZrDt92(7n>zPl?>98_+Ez#jC#1Z@7}}xzlw~QE(xKVs zRQf0J<{&_$JYhCJ-z6ME=3$uJ6xW_Ib%&19yn$q8$0i!sXV=`NptAgn{&`eHwWtNV zNdqio+R-t8UxJ!S|Hkr`dE}8F>vFnYPypsWU-SmpFMJ}ezh_EoezQc_=hpl^G%$Qw zkYq*{Xj*k})rQcpi~S~UuPNeuFJtR6eQU33$4m3>j_nH!=xQe$L5?@1O0{7ytlE+M zY=PYnj4PuGOfcuxMCr&~ns`k?iCp17^bIH*qf6(CtOOE2Yg1>jJg>ac?=Vcz))}+a z#y{~K*L}g#-aG!|dVBBU*utyuMcS|H!3+%KPaj{tOvZcj;-|;-ydRYhUn6dK_s`3b zu2zvjkjOjXFXi9K-I=wySY+Ylr)H8GtLYB>heNT8Hyw=3vXtAkOX@|90TY^>%LG;pckNwa`M3h+XL*|UE+ls)L`TG4dU;8d_Oh>}ivrCS@-v6f;z@$Zl z&=MH2le=5M|C8?`@ZP}J_}<0v9^heEvTs>miQWsfN;HJ7eBA=D2Bcs}g%A5P;}q08 zQrRo#B%UYVcJ zv0%*v_rFC9K$u>OJfUAze;KWQ`Gu&Tdjai=^5sZ{%Pl^XMcNsjr^&57JrooeyANd0 z!$0+|w=SMal9&BhE^VW`8YI98i<~tlI1)Sqv>QYz1Joo@BZe5lz)tPp960O_3s$uf zZDkEx`ojw@ZNj;cZtU)r$O^95CJzB-;{GvtALHBXa#N=ro2U7~iRln`w?0u{nm-eejz?X8BlFskuC;6?u zQ6zt=LtJDN`PF~61MmwK1ila)s@6I}jFQE$;6IP+7+Y18~<{MF8rp#F?I z_%YqzTdvqA?dUmelJDgWY?Phw`~Tz?vsnoU$)=Eq$W^+ z{Tyw**HL_;fl2ZV!wcclhvb?xm`d%!z$*a;my4PPD0lzxY6MH|HsXXPotnMvJMvn9 z{Wqp^Av`eEsb1~#dz8n7=UOJjGxxKeqG!j=i#{x5cS98ML+$$fs~Avb?M;PhU+dqW zU-NSEXeex4tnHsLv>_deobnB^dSAUyV({ne%z%-g;IsWsh%g*)Aq5X|@`4$k7oysz zgwj`dAT0@PUzcQnFOI_4$XxTX-%f~#S+NiwR36C-2s!$C;xeZc$6Q9FCN`z zNhnCv2VcUC>z=a~eVT_1rh~J?_zPI`pXxsSP6yMw$Lq~P&iK!&J^OF38XGMGemPbH zrNRT-R@e_;0vy>rx`zwqEV==dRsP1Zx;`5hu=4ozH`F~r3~WS(BqvfZ(#WlUBT8ne z?ECto)0H>U*T^lo$tR5=L4;mYVK>>GsvyJJ2Q*PYHnJu6<@+v~$_v9PRE>b^ACk5X zyNeWiT7KL@aqQN6Zc~rM_(-4L8q)O+Crz4Q1YxG=5ITQ;R=>NRJg!4zVBoQ`aR|sc z=G_yGT=N@AAsj&mI(gBgu1BM<(NM%17(DA(^G(x>CJIXZuX2<5&terE_t(z3+7~xL z>;70(s)1Z$H5BxlE;Gwq33x{mxhHz*8oA=F??)ZKa&LC*z13TPc{$SV@3u7h@=6rW zsZI;f`Q|Q!nf$AS{Zk13sb@pf4EW<&C$5qkT=qrf$lpvFNNU%jhrrez;<^mv5WqY> z|C0TvbK~iEa`LZ=QSvo(IPH(GO?-LDP5t(ReUGXqQ3MPE<2_dTw^w2=;_mFJZYq0k z>5xE>(FmwHoiN1I;pTh?iNB+VN4;L*fRopl>e_QJW#R-z!t#KY#wYy@&s&ISo^$6| zHOb^u@QfW2jvdG;Z@%YxdiP&7%bzg-$c^eD;t=zp?WKyu4Gn??V|+>CyDlZvcZIE{i5AMG*yj7dEy=$pwE;CtvY{yk?Y0R89xlPdxJteQl}GI-=AzY9~O zVg`rrcKMd_l#0?TQMq!9pKKYI(*l8G2{cmwZx#BV{}6EXZ*k+)1GG}1T0z41LO_qR zS^w*Y`OkjsfATipcm8?VAOC(?IOo3?0oY*wUW9)y!hfj=rOml)(AkfO`uf$R{{D^g zi2tK9_D>ybSr7c@TJ`B)*Q!FGnq~T|)`Rh%C!exsaW)^{C{%p{&+0sS`XBxC|D>Y* zmn!&O;P0J7dh92g30)qgssBq+zfjb%<6WM(_DOxW>JXd{@BdzwMNV&B;64fy2&s!! zIPE49V>OV7bD|SyLIb*VjP%z9?be}f*v}7oOOc^G{%lZ#^Z<2B5W3xwl%WbB()OV1FtNZRYP&l zQmVz|QZAB!bU>t@md4pq)pfb*5+8Zi<)z8M1+UXmPK@Xhl+8R!S$2lOG7+3`xQ!9>x(ZTJ!Xp~ zDn>pzQ^yWhbCz3`sl!%vno-frrsWl`GvzUk1K!r>R=?*`XtTH<{=x2sZHCADQ7tv; z;8Z_gDtn7s7)bxZX4Z<|>$n4;7Q#;!0DQ~M^nA^pwG<*9s)moaf&wlbs8_1ut5(?j zypRUl4F9>}rimNig8b8{v~1A0nQ^izs)96GirIx@g>b;C)f(EOs%M2@`KgE!n)3}s z(xL)kTb#9&!gn?$_krjKTjaO=Dke4H9iP)*^m&>q<`V}exbXsRB1!*o4;e~62a+In z-AW_({<7JE*D_06HJrR-^4Ox_y*~JLt)TZ#N}&p9fcw%ePe5XKfluvmj_eL014-P!?nf6WUtyA~MoGr_2>xU9_yDfUV@o`WvjieB;T zS0lpmyYDphBLI{12Yi!pFfa*{`;Tl7EyvKlCG&98e6Qp4CR5o~6nbn1(2oaU) zKJt<}ww-dWm^;(3>Dvre7|wPkSk`g|{0kNr*_}G$sZYW*M%Z>H4LX-nxY(`V6{|N2 z6E)vIMbYn3kJx7V570u-!iHGh2vibI^?*qL3NS?HT;e!A6yN=s7^2;?2;5@PlJgF+ zNReN?R6IUXenF3YGrU&^jzSYXW;D!ys41LnnVL2bb{pquefD@k4pY6&(Qwv_yKg?E z*>$r21uvEp3_X*PY@5fW$QMi|(6jw{lss;Z^(o1w-ujgz9N21~ih^$f0cioUI3zXv z@wPNBMOUN^U%Bvjg6W#H?(mtrI*WgqxEL2dQBPahyH{#2?G?c!l#b>gHk; zh8u^b>VyZg8Vm`R|6T$EmUR=J!J@hMYJC}6&KlP;2o0clpF-YN+Yp7hmrquXWhv{3 z^isF+h8+)VGYM;z*fie{>!ZAeZiDi=3rxED)+fI&`JC4^YT{LOx8W3FX@CJ z;pe-)sDk8zAU866cY{T21UJ^QI#^A|07MF7aPZjUB`xdbL3U;FDBzeddPToM$hs`A#*FuJO~6(3~-J z;|@6gXF`kR?5__g>k-7#rnx7gDThpmfzYMV!%?+0mIM1>IZ`sTw4njfZU9Uc+PbQN zpkf=7=415V{muJ?mqQh+cs@OdydA5)w0dMe2Cr7^JU0Y430}0XNoYD*HALx}?1}<~ zkfp>$;p9*Y(l0StA9^-}_kdlv@abrXocc^APCB8y%93<7>>XaKTIj(u)WT5!6}PMG zPKB@6`2q#*($95D4a*klyV}AP>n~1bHuBP!<6lf{zuVj`V!E9srT}DtYVYEi)Hmat z&OXXv_Pe-0c+JNOm*t|&a{8aLfSXQEZ{}_PIjd6jb??E*h!b6YrnQD1iBs-ZZH+$ag`1?%Jy_NT)6Z)E(B$d2I3x z3b!}9xtgpMvtUF!m-9jvZ%PooOv_GPdyPlV_P4#MO!$7TVPG09ReU)p>S$KZZE(jX z-GimJAn}rLMR!~Lhysv8g6c4#yn2Fbr}wln-zu_me!mNBsH>rypjmfI(0~-$YCmd& zekU&9$iKgO@vB#5^<2^?(CNP7`AKPlaz-c5?m56nsyUq0m4AG`w)Bd0<2c^L|ET&q zJ!MnQv`zXraZ^{k-WW2SpYK;&x}=$BB+sjrA3HzC3BboIA2fhJoImVcdR|!iAf$4r zf?NHzPiKeC)~yIZF8)Ou>5sSixaV%XcgVjQ@v*-a$v^up3N3ZKo+GAg0rhPaU)yr} zTFUua(ODQ^MYSPo%({AW<~a{*zK<=Gw?$MAC`=MhkY|@{Ej3aqYxlFd5rnwDBBR|fn+@A%s;p_o1_AI7zpt~`8r@M_9q^WQdGTrkuvtlxo z$FpqKGqt#4{x}PeKJLHsuA$D$c76j_L?3F&9K|#c3-1;eV6qs>iBXMpDm+}Aff@c2 z8{ngH9l=QcYU1_hQ%e+d7r$p-A5OMS+hnfS86+D0?4P+@R^E(LAlN2NIr(uP{N@9H zd&$2zXdo2#`gqC`^Lm;;iiRUsAk*D1uR}wlIx`MlA=kfoO;s+@)Tcbo{fdsTI|$`a z_`NE;$a0bH&d)0XDI$7dC`+8N3=m9U0XzpBSyiuGGQ2wzXshS45(DZy13nT`A0|`G z5L`JZJ@|l=G+Dftx)ONnT~=2bJpl1mG;SZnsIE<&weY_%&5^+$Qa;?Q$&3>V9Hc-{ z$pCCi}Bn^ExGNvIiIa^)svjf?1pZPX8yu zc9X4Mj>y}-blWs*gx;Pw3^o{)~VJoTgYvt!i;5 zTv}|QqSFuRxgX+G(;-Qx)AKl5UaVV9wBtJ66v}I~yL!%;0_;c#XfCi@a^7u=5Mn<_ z!ch?|iANJIjfD2J{TUw1{s40|-HM3Pp?ZrhOvqhu{Sw3_Z9ok&cj22E1Oz8>)tv%wXqRCG@2= zhoPr=Nq(+``o-yz4xptkVl&B8zwuaj|CY61$kSH4yl6S+(A!c9UBt3y2gbWUqcoo; z+6oB-4BtU0h0xH7Sy&ApTy;s%n$h4?8K3`G75f!#-FSxkeKyWyS3@CuSMxC)eOpo( z@gk=|kSHZveYX}3W=kx{bD==$(__=e00P=n%HrbNG141l^Pw@<&nUnTSmIM zBW#w{ARKCP(R1Dn!|=Y*vDPz`%jZq?<%U)qF!bE5nXw7&!yB1=8{!CXsJZG9Y#~L{ z_@-IGz1s{FGhew`IHn~Qzc_b!(SRg!EkB*G7Fk(a&!$qgHXUwOwi@0WfrA+Y^B6a` zU*L}n>EC5;m^5&3f~p?Bm&=*pJE`XAq| zLvFDum1SQFbZs5Vql8qDzVw$QnsRd&eGiCf3md}_pM{Lt-8Y zG2iId(qS>OSPednaizV6bX~oS2}}1$V_0vTdfB2&Z~tVl-0b@ui;D@f6NV5jT(aqF zAhGpnrkBNwy@qqBZ|JDAq|v5$QGV;y0e~Q^phah9RD8XChmx*r-mYs0bx6KGoeYF+2&&M#fxIsj z%wy^sBb;_$CZBIcV(zli?GyKZXelvfy=&@NO$~X_OAM2uiV!V}i^8pA`hPvtuwZU) z)nXY2H|}oNO#3XWUEH%QhEss_!jgf}3#ui6Uh1w{5W~D0=n!Y0vC-y4dbvUm@D9t4 zn^4EHp%2Z{$&6qdPPZERu~;Lfl`r)`{rt=r-{P1dIb#*!Jo;7svFiDb^Hy$sYe*_( zMpNm%5JQI@PGcR>vjru(K{`fyAo)lt4WCy@+8&U{1Tq|b72u(rjG1-cRM1r$y&q!P z)HG!g@u|911&aKsDSPl~|8xXlvJ5>P&9u?~outy?TqNq8qtC4-xBq(=7_9Xx=yy0* z*>yi84Vvzp?RW9mhN2hPdlnZwUV6@{YSV-UV=bfP1Kv`0?9byVT!A@Xjqlb@Em2Jc z5@LACqGNCD)Q zmIoZ!`$ZgZwg0-7H`M7<+XP}&gZ()DCjESn)Ln41BoSbIo_-I)q?NdF+g=sM}+;93Udl*R1QMIJ&j06FQx$a*tWdMyY z6;%^WyU^_;6|=6u%8?sQ3q?NpBZIkNqo;`Uk8XMz;%qT4CN$CW+<_f=6`AF?zuq2H zfzK*C0uYsYDCu-rPG&zkuMU{5-tucW$=#AUX{Qt^DWsxcWQvpa8fl0ZQ|GK%2bFv> zc^?y#+CBBA`Dm3xCjF88CVf}PRxu;#6FT}zczY!Elu&iw!4o?vn{VGaYnL?ZpoSq@qxO)5iZrK zd4;)D)>Pd12U`ZfcEpA_ANN%0iC#co=%je#m+qe~>5JTlx$7Evdwd3QH@5Xko(E)~ z=omWjSOmu}E2evYu+itXY<J^Sm`d|+QZ*5P4S+*h)~3bRe~ z6bt<9!$|I_Acy}xG09)gbclRoL1re?+ym6NS><&g5oTLL^|9jy%M%w=nyVa?D*DcC; zi9u=eA1|9Tt~f+#{iYZwU)Q8WRom-Y6fJ% z(L(#GJ(Sb)nP-cbiLXV`4-0%Q0Chyk9@8sNZywIw59vZj$GiMaXxym<3|2U}*&EvD zN8%w`7x3j*1kRN6ye3~AW``Z($TYkcGMOLyPHr;_*xVY*&7#Ql z?`+WJw_RR*Y#^&-d!X&AC0bpH_j})UY5~OSsJ2bBILV8`j-I7>v{n#=HE6wys+ROj z6WPZ)v=@UPn?3=0CjX!OjWwJpOeyHpTUvJ9mYDO?FkMgM4;w2=Pi|rWLyH};X1{{!K*1UzX!em&X?Ssls|R>1Em;Zj#%90z8lA5=JueWsZMN5s{`=bTY5 z?!2;@5X<$V>WG}0;U#wNUT|`8o+ql9q`Cv7lS{p3<(b*@@RWLEUFSu29zj%Rcz!p< zLtXg=LA`+0bO?8;l){0k)mcb&+9g)*{CA|#96CrH|MUoyigOzPi3iMv4;QIqxAT_Q z{F}75$RKlEMY+L+d2nVGk*)7?!TkJVqRwAuJ!374yA=tEa}HwVvIOxg!^; zGCLzv|=jvZeaST5htoCpS2M-MbOWmdSGV?AAON z(;h&VK6qa3~dJ@2Dh8NI;uVCx#0$C3)93 z2;`7^o0@vmzPR#<)b)#VWTAEkbnj=X@M$vVfY{&#YeJj3DCgn%PC8W`v+_Z$?chfs=TXURDROO=*B|x{qvRAYz!3l&2(Bmhm&23Oqv*%aM09+ zoSP#2^Rv`5rPtJ@a7Irju}E1r8yPh<4T#5hY2jD>7}SWSLGrUX1byE_-O{-?EV$-J zzviec)?(-nx;3&}wvhEX^`x1sOVN;dBDycxcKoA0(CxphJpE8uPdFNe{V+c$>f-eO zB<=F%3&d-BETR_6yEfs4N<560S}oS1VQsbaTAck_&Bk5cB`-38ic?!uSg#kcJvO!5 zdPqf+$NaG!vQd{h>(CD(j*P9{qEGQ4qsbcp+D(+B!^+uDyTMd#9g}3OoB)*m$HlH0 zhLL9)K_Mpo$^o{#kn%pobl{U6gDFJh*fYa5&85y)CeM?2pl8E=*d2(?RzfPvPklY1Ls=WB-rNsIN>1p( zcgt9Q`i;%66dxm#^=>w$QVWl5(**f3V4M&{n`5N!c{kyaou5XJ(F5ffYFr;={^8!C z?tS`6gpLV^hV0SpAb}Qm6|R{o1?O;=iKf4F)_I1OLozaa3?35^tspn>$f-Hz?9n$w zJS}ZBq(f2fyX0?>P|K<6Zb6!qU4_1VzXaEdbe}os8tPe#vnUuphq}i{yV3CkSWaB!X~=KxASBN zIgKkC?mQ#&x<)0FCaGXfOTQ?G4}l-Wl7{hSM8v=M2cG16~fh>F6r7wpga?kbD%lz9iKajsn6n_L#i6!VK#@Yoy^O|d-l6>d5U zKN;?sF?49Ix2yQTggwIwCkbREElXx?*D1;39GQ9?koeHP^U9lxCi1>@Z*WlQF~U+@ z{;IX%9C8pLQYdL>Avs+ayfN}KmV%YAu>u;t%LZBNI~nqGR1zWKaOXGLdWsu&XKVl# zpv%E;%~yhV{!mBR;kCXFlkk%Wl66i8+@cx3M9r6>vQL5^j=V~~Ln`}3_WkP0B7H`d zVv4p9cVU>gD)dc!8#&5@)-hN~Kwd|2HK^k(coc?2s->NLS;2u4oSQfS@ z$PX3nWV6XC9*)RQf(8!D*$$4)-o1ggzUcniK7dY+p3|_EU5ksQMx1RvpzxxfsrUB< zDQvXa^k-P^QJ!zuX4XrHFxGg25jpyRV#2*58~gD2YqI?fwJR1zZD`U??L>F4a(q6u z-K2os))=}1BWjeAl|xVFOtnt8vEQi4-F^?S;RYpVwU?}n!HE<0j11}0wv+0{sH3VD z=bbAzZmuwC$Z2YNx-fg4Kg#?zU~0mc56ksWY#`sTHR`=eV@u+~-4yG1UHR?E2hn~! zo9ekU!?JGwLtRqtj6`snpddUKdM(?)Ht}66ugQvJ;ql@N2gs8#6$ovdU-wrviR!io zW{;n!U3zPyiaMoQVU*yaJN{|r3WKs)eGgA#;Al4%GyGpxpSZ{7E1OjUW|r2|?KPUeFiFjz zN<=F3mt)bgbd3Gy?1Pgv(-{b%^2_DAK!4!}GTfE-7*4K-QyAnBS{cb=Xm)x*j@yJ; zUyOXjvXbt{(=MN-AbJ~iNl#E&g{Z$-Fte`n+l?Ql1=?KZY8)eOJ^n4vBt~{RD%_0kLFMv@Bs_Xw z_DR3@z(;HA$L%Pu$!C{|$Px{2P_H2F+^Bo^d9S4FAM`Q<_;x((hx$aUR<62-V(l#0 za1x>KlwInBL*bCm4R48N0Up7hb5;9H`w$*YtFe-6KxC)_v_Fb#iVc{`2?*#mK+Qly zlPnVV|D0;h{EB|*L0M(DyOx=%t!q5)P`$$Pma)(DnXR=0^GSH>UYe+0XZGD7d2=D} z@u>1lsLbTqc*!}tF1zIh?CT*TuAS673i}aR<*#$FBCVs8w^S?q<#j+>`{~UqI}NWw z^=3IRnf1Za@53l*jh>kC;_bzL{*AREkQICels{d%mx2nl=yJVe*A*jCiK= zUxB^APDHZrdV7O;JM}x)pZ7kx@9c1g=oTNZD_U$gm^l563cs2vZd6qVFb4!!UBJc< zx^jAFf1QNf$&7tb7d^ed-z$u8^=6MvW}UE~87p$^>iM!QXBxG%`sqRGjnzXxDy}+2 z{azsNTTgabFC97J>1`j`5_|S;jl?4mb`Ti+gXb#I9knpxz_eM4gyH<7Su=_*VJ?)~ zX1ADaPtuR0WcZc}x3%DT3~;f5x>b;l9c#bDH?8>Rj&G=@#&Qo6q4;4H+jihbtTZ<9D!; zmm3_<=UFCWCC_u>xB?m3pB_wA&Ra^1S;jpmZC;Gb!3N$HuKU)3M&o(T;1Cw>@9GEg5`U%o$Xi^en6DvYa5VsdZD zFmFz%Ump&B@u@de1;tRD^%A#Lig=Ej(U<@746cNc=r3{1I^KbVZ{Iz8Zi@w&2jC_5 zf|sk19)=z<(hHL){Z6@b#d{)h_UAG6vJw6LlV3JG-Ad*e?Z=sQ^yV~{oXxwFUy7PG z5x5r*LDvuaw3v2IJ^Hi7>>Iy&lCGV6akbuwareLP_@}b@#fYuGxLU^9kHRR-6@OY! zt!2Rdk^ajvd9(QT_|!0N+O{|srN%Z!1wYCxW548j|3j|TF)ejh6AN_DNDob% zz}>f>-av=AGrxIR7DxQ;N3tDkO1$hgv5YSQU8*z?e5j>pTrc6%H=nd)ZzkxZFqQjd zwA9z4d6~{meVuaOilab8_9f%T^pPBxY_>G#vvRu6eBPfc_A?};u5eA{M9a$mAUpw_ zj)h#MWoq8f$!V&ui}I?9V4ellocUTz+3rHyZ!}pw6yQQjhr|HWiibV9O$9y57jF5V zaxOkrbXAM`$Qxp+*!Rf3Ccr2>>h50DuHwyb76#NC0eU2)mV(>m9zE<`j1XLQj<8}l zhFdmzZ0(KDS$Wno%HjWmJ4)V{^TU7)w(5P~5N_mn$R8P9Ec#hWQIHis)qeGf%z=eMtac!~c$Y8CUc z;zGN1l(04ahuRIck{sTbbE@4`cFO|SC&UIEECS?np3^}r(OMdcmQfopxFuiAv%MF> z?{-LhN3LmN9x2IEE)~*yABnGf6C}27M{1&r5565~+FOfEo*bmD6>D`?JZf=nAyGsr ziUOI;@uxUN83R^ok&NFijJfW-OZ7bHNw>k*LKm1ekFo}W&fGgz65~+6Q#wAM0AtvO z{%>&cIj)Ou%%{q>kc}%`i8&9qVyiqpwCLS4lUg7P+x|YedqsdcU2&PSa_&?ORewe? zHe4{S6XWoGa_5-_ek72OUb@JOHkkC;se@`NEGnS9Y73m+|F(<9+41Y?G9FnO+kd05 zR7erUm}7`j^6l)g?~_P0@iD)R-i%y#_kP|I{FJm`;$cJpDT zxvS#1Z|f=tQG<2?i*o2I6jX`Jv@){ngJ)}KoHIwh!<58i2g-Hwk;s!?^=Fh9tvCc4 zYbzG^IGpN)YIrXEl3S?~=Tmxb)Is7Dwqw&$uP_PJ7mvF&6TE%lWG8+LnWLEDaqJDo zaw4YB+}ii-EkRG66$P^*{LjL|z_Enou;`Q+9eNp?KI8v~sI%>N;4bq^}4GM^K zcek{H!~hCNcMc5#g2d3>jdXV?N_Thn&_l;?{yfk7o^zcqe&do0hJEjQt>0R^hnn%x z33f%@*~ZWbO!6#DE{354pk$Jg#8$&Q|F@4TExetVE#|xRwxV=nOEqr}JGl2f};jxVIj{ zhrZ*9y-F)LT3xY|u0p%CO34V#vR$ZiBKRPC6&L6*B1Qe#u6cV)^Voz8&T(?F@0DK) z%OIW1Q)yYS43BaUv|Si1)XD13kfya^#?LP4XuzI%C@7B4kjQO~ALw-*@4dCG8G4bM z_Hz<~Fd;iiZ*r-^kIOH7Vqn654pqG;ubRxzrj8gcd-boL8JZ5>1zjh@Y_fvavZ5G@ zppZ1~+5;EYX2sFUR^OoJs}=&;{`+h30c@c!663DT(U zyj3Up#93F*XUZGGdtUhAUQhNus@B5k{4#kXFK7o8^Q;*~Mx$1Ybn^VsnI0;wSc|MYxmEV4QCf2_F>}P9FClAak zzCY8H)Q`b$hEwevSpA#%AnUsK5GwM?g{2*651PKPH1)ELyWv1)fL8R==}Fdd(A_K6E!(Ov&) za-N%4y*lg2xPd*ox(0)qj&~b(z$bv5xX1({gwbmekt8!hO}2zYgvi|ZhIvN+MsCL1 zBl~U>!RNF%58|8|ZxLKcF(y@Wbr4BVs+B~BN8@pX_8~CfMe5^k;TXqJ&?)>24Dj(FM(EbGHH?71B*|=ex*13gXA>C>FyOI&aBh2eyf=E{6W3)QGx|J=OXB%Y z{Rd+UY*e_&hC1oo>b`>^gF+($Q40T;?d0D|qC<$|RyUrw(+UX(3A{UyjabH5U^FO3 zrVH_5$IIk*7hSCVAs}qC%_*i9jUrX;wxSpZ6C}EGZXUUpRlB9$E@#?Lw$y{B>RkNQ z7c`?RxNX_ZhTfb^j*3`EHLM};jR@OuFV-q6gnx7ddt&Rq&}nkVbKAP)*x55$`7J_PJfXQ+2HCty$Pv`I3P9!M?emG)v8EXP%3* z-YkJJ*6bqI#-$}N*Eu4R?rx{ZrZC!<1*~>EdcCTC z!EB;pMVBNM^h8r=HVJn`4JPR@f2YyYr;m&&)4oIx1Kcfdtw3$aQM)d@2VpI5RaSXI;G90n$F(Y zm9tov2t<<`clunX;mw$}Bi^J7UIJ)&WDl|5kTT1Yf`0vahJtv_ZQx0mqg@mzDUW}8 zHECu@4OwgRo|*xqbtyGXY&u8Ui~qQq6aSARVb_R~1{pltdLEoKQgP7xHUiSd zX+<@K12ldhw{im1~89rKsKH7VSh;v|uh^=ui+S#9Y3j zO{3pZ7pbCGZo5DJ0Nc^G7u1!zaEy&InGuw9(SqM^EbcF!6e~n2A}LHCl^hvBj3Vs! zM9vu0TrQpioBQgIeA_A9+oI!axyjWHfga+0KWz-9RWEjj%7j2V9wB~xALt-4pthqz z#E_!fj+&7d=N8}_`mPVANg+7oEth256U3o(fucAQ)?_K{F~m`VcM6S;5j+Bxl8`d~ z0Ts^;ja}5tdnwCa^M1x&tNo7^byRRt_Uz85P3rN~?1q!*>licd#N9#9DFOTe1o!bq zwNWHt`I4H+icTi^%HJ1yjQy!{clg`+s3GR#{fa)i_cnU)sUlU>Y@pKA9?Lo-E{l6a zVA^K2qMckn1zOYz*Zhsh<68~F=?46XdEQg}HC~#l0-NIHw!cW_){X5TPCb90ezGqCC#b^f9iaZ%XVRZpkpSzig?)>OkVFWPPa0A9ebG^x-$g+e}?un=^fcUoEOc z0ze~13qp43iL;?m*XjF5dHb&tRi4KwiX&q?;&E`+6^2XR5rPSha|UpdVh4Mt_fF7q z%kgW(l)Cd}wLyCvwrF)tk5patfb}|3dv1@6@t^=?wRPwMM?`APmF}OyYU~gu>CIh3e8xc8Q4EQIX}thI_*inpZ@ecjas%yB;t? zdcM{~mi=w?H)AQ4){HpK$0iv} zi@jN+xG^S;%O^cjj<`JF6GhUTRMWcq4{Bxz4~>niA=N)S99tT&lFI8{B1JU(i~ptz zeIkDwqOGY&`RstMgOfggeF4iyog7To(b){RxPeS>JJgWDCU2p2J()|KQhRswL26%Cg6Ritrs?y#b>xRUj>gxW3ZG$Qn zcG56ae0gNngW<u|IdZCzM`A$OI0<=sz8fK11uXz?Ak0Huxt~ z1!x=;{MSFv!oBekP!VoaQhAq^lELs0P(xN@2E~M{aK`#rQ!VIc1s@43#8_) zUjEd@SQ3@^nJ$P|cU=x0CG=5;)WfJ9QHWz6r8Bl5PBIzV?H$o?y9^9jQ0YN3`LOe+h+W2b7YSL5@X$K+-O{6>y5B?0I~b(F8; zZt^09y7tY`+{B`1dcji_?V%z0a1?JWJ~_${kaPQe|5C_HOc80U{vL1YtsdcVTphbA zL!lR0todfRqB~{@eoQ*#D+N@1%J>0ck)t(iQ>#h&rGGUBja}<^25WqG=P#NDzQq&F z8j<_dF3C3rMxlO9A2Yo6cw+Am__zFW8N)j8a@P8wqQ7%m+h{n_@?qo`_n0*?>EMHF zoLeECQ<97@%KIQ+>6u#JcqPi^S_%$>OlHNW{z>zK%(`AH=tm19?UdZ9+UgwFPUIGO zr@3HaY)4~V#Y+C*T3TGNPEX;g@BOeW9lp7|o_&Rx8#7i)L|;loK%am>;F^9ebvUcW z#5d8xZ3r5F4iq})NR0k@pQmu@=A$36bMuvV(CJ3EVnK?U?88=O( zZ2f0hru@%fWe#^-dt2&d0>TZWD4XR$s!0%`h?$Qu%Go{I(Gww(_Eg<)PdrQUP6d2o zBxB8n0mh^~Vt<9^8_fYnM(b~ds9|h=A{&GQ-o|tN0 z+~lR=yyCqL(eo6Qg&YwwYwRdwE+?IsF3H;a6{5ySbL}{eBy-pfIIH$V@3Aj{l^1R^ zxPogU#J7gIcNCW?CXu>bR~R1UXS9WkG}F=|+AaOp`^3=aRK82qu&p;n>^ng=C1(P1 zsRJ}C3m91WD(NAIIt=WN;wKLYu0E;eJ~0iW2ZBG$zYRlhZlk+B<;g>$Q+{7posqZW zuBzDLzGh-k-xkF8U+?z-KF9qxALB81Ds>$XX?N0S?kI5Ybw4 ze-c1vVq{%=7Zj*(Pfi&SvFUamn|S(Oo&p>J-cFdkv8-slvYIS#W^^}P6lbl=`#|lp zSS*w2Yt52$uO~npjAOgJ9QcpZ^yfH1-q5qKENitLGZpX^UPa|YuU=`uZVos>V2EC` z@Je9Eri2UGXUM4!1hqygqs$4rGD4)3WDQ^BnA|IAv4C>6;SM2WcVC=oX{DaZxuI$W z**h4t1f%r(V+;%YG!%ZORwU^d$x#}Bde@Q8c-z5HF2=>amS81njbhQJ^d(dyL)GiU zxFL+l)L3A3jI}W;N(!?dhVZKCs3GmAM2_x#*}a{6`MftuQ^-G_zjNaaM-4k!4dVEj z@+*}dV@X#xy$pb@2(~>49~D3dydwPbCmFQ{uI;m(SkcIKuQtYP#+f0nbvo8LpEsKzOQL4v+r)?MtwskYI1 z@h-Y%>9EySHreShAGRchbQ$ggJQa)Dtub4Dq zCXSR6n0U-C3$+#_+II-jBUM*R!a@jUWw}04rJ!{0in|E zqB&uXg2G_Vkf=xEKV_&m>)y^6^50TzK1!Yur2a+kSgH2}+v6WtZP)tbq5=Md70Iq& zR6;um@+2vrnPk;9?65hbLb6urB@sN6D@)HY|^*_FSAm9e_v7 zfG?Rx(jII{3ooE^uncyh2(8NQ!h|cdi1q6gprynkhYSkKJ^521O>~_)T|#k;@+Z4a zo4g~Ax+|%*QIPtPv6I9kd^(o?-29burkyEc)t}j;+FJPIHYGg_xJtF&cd<5GLeHA^O{F9eo${t5} zTi!27`NgaF#&*8qFz{lI7Qa6}> zISAP7D9$~yT>fe;PZs_KiS)|lj7o^zVGqny3iatyKG8EdP)_#J2kDfK_2ndIQ_yFf zE{6s6S?i86sC(}2i_ojEUMjXZ3W-+Q`P2Se>&g$za|(ZX_o^~|(uWz!k0p9i7F^!x zt$Z~hqH-%Z@}3oFiRr))@Yqzb?h%SnMvUm+sf^(_JeioOYuoW7GxnGO?3mGmhBu;> zco|AmDRjrpp{C=lqw8#X6xR^31B_je?!6V}E^LlX<4ZnGM+x_+NVx^YPzyYFNyM6L`Nsr8w=8vcQ-U00 z5j5`zm_k%=b-a(Z89!GpA|^?;zFSTj^0k*4Al`BXpUT;U&WQ=REi9K4{|m_78{F5I z{sFR+7)9uM>Gs&D9=49azv3!cDPnb&S*?DVX|2$CXj(j4(|BG}IzC+|toE0W5RUlSRYMcG;rP@NUh+qB7DH zHJ18j&1#nPw`&rBENdLALipcF^iy_f0|^_?|1@{jsFJv@nmn4r5c?muzujjeXe4^2 z8v;M0TB`b5fg;OU2!w=`mBeLEk{PSu=KAiK1$7so$QKPSD_qX0T}CKZ{E{d zYM4r4ad8-s6MV)lYdaqxxCuwS-CMMGl=!eNKO=@^^f}p*?(Z7#J^&T?!Gs>&@PmT# z^0u4tTf`7+2#r6dPD8dYf>;DQ?t5d=)UWA2!&e2;xX|s?!fx$@h8fqV{)L$eP-}AJ zPH-Gj*B}xOGH1Aq0*mRt>`zH@lEbwQnGj^W&yXLLpQ8Z-&G+@u#~zlEa(;-5|Co0{ z(kLrTassuJjg^(dJ1>&YCDS+bj-x+B<}D8wL)wx+(iH~qjq}GOxIs0Mk?6|D&XCcC zk=aDL@!tnYx%OVQ%B_1L#?T1opq$~@$M%XVdBvW}4sqFlVGBX!_dI4!jR&` zaS(w%B)NJs1_6d-L3tnCUk79RT|R#xCR6Sdg=p^5jR0ItHIZhl)AqPu2&nkU+{v@W^E!y_VAMYtS2b_zGEJeDB zD#7h5efj1^GLn&-dAoZuwL#yNmr>$uNZd|p$lV$({FX~|As%qA3zgUqM|^*#FIBO4y!mM;ilFaa?WRN#@%|Dw@zS_1Kzx~_OD9*-I+eXXHfte@MbOQR4r8wUq z;cw}m7v!SG^M8LPlgXKUZ?mfyu)ZN1vCHyL=lCD#m+{Wu&KRQw9s#ftK4XECAm7v8 z*E;X}gv<_jCVM@7Lh9JGOeK5p@6xj%7ZdD0#!dd5idt4fyP0ZaG6C0W7g_%EG^?^J|5z3gs1rEFLx92R5y_|L__}1@=HE2u+GXN zMf^rVc_!2u3LiZDzn_*Vf7Hy9foKCm?I7;*pIMTD#Z@(dR>P8~KDw_N?4=K(r&kYj zhrMCd?Ozoq_J8LunI)PkydLt=IB)iTAPRByM%+dE1VRo_59tcz1Qof%{G z#Pk_m6tI-TC7~9f9lzzYi>H8Jr`NvUQ+s3jvgF|5kPk%k&N#+b-ahpm@uy^=Saof< zLOxYk4}+>ol`P2R%&fwyL+7s6U;VO21!qoDtgEoAke8D3ZN;NUj?HS0vrD+Uzw>Lp zukqm$X|Af-bTRT(I!BGGD={K}gt^TcoZp&R8#i64 zvRpd(T^$O#c`r|Jh)a1ID3wkoIKimQmQ;`de%B}LFhvHEo+M6|ygwLr8tERT;^RjS z*5VU|y4o9jv^x(gt2{BM%wks4^4a?si_{H^7`-EfWp4hv1|pn}cORLQ zUvg}$8y-S8KE?{d0vz##9#hxktNi?b5LDjk?z*q5B%U#zBs01?$GdkdH!KP)Cv+5z z)&fcvAAbt^0W!%*ZLOVgr1!oVFF|iGb<&)|&=7io!a{~AJU7CslD+2HF0@Ed4>-U--tUy^n-IE@ncDWIXQ8OJ4@gVyiOWYNuVq>s zb5UZ#Fz{a+ze5p?_#C7i{FkQdw$)yZox_fx;|K*Sm5%x&CicFa=TqI<;K-G}-16tr zqHhFLZMm4fRwhx}b89hHSmY}a>eXZO<{5SfuF`t96|c?x6R(%7JNu-IJm|(&3i_zc zw`L3t6<*4dxO3Pf(6@82p?(+_7G_pPaq2YCn26_=(*Yd8qNkhD#9jVP-X00gmV|SU zteyLT1**sPGf~?n4S-yC+o(}Q{BzR;bY?ffwv+Tb7`nI>pB#g?Zi;S^EH;mY<%P`- z*5XvdM9jHH5Plih){D13D~oO>jZ1BxrWQ2AxothZA!GPQL(9HgJ{=|(?56;dR2q`H zf1M?Fr@_Sg+7I-NoKjxYXzF*si%)5=k_oaQZ?+mBPpI|r_6SArdMwRV%|D#!A4Bu%7+?6=k-3J$+N6A? zmIu%TK$%n~`Q=E3Lw~5t^F5vK24f70p%kt#TfPsgpN=(4in)Xy|2VM$a$uK(lr8FSSpj-4PjB0~C-hn>(5Gqw<)^d#qb23^Y(nc1TID38vG z>lfAho);P42Bu0>=dmHeE*AEY{>#|L4jK~!-wMmle;<^V7nVkjO!lcESqR8zFzsIjxZyWZVJ{BMU&YI82l{k%tjm|-v^E*!DmpI(&^q_e+-4a*0 zu9YR^B+sO%!VWob)yTDPlfg9+Y1}IKd;0;bSh-(=ke4kK`N=iD~vc!pCDHquOgFDqChaY-2v% zqwgOl&_f2%5MAgAGsZ{Bul2t(T|D}*cDU%R`kAk&Nu}(k#ft zyz{{To)R@{vxTqcp5gkF&$X{Tcsu!Hp~Q7alsatKPfk>gKq&Q=oG>G-v-$Hb1Q-pX z+7uY!4~{_e;2gr&hLtVd=&B*8*6NG?o<6gFaR21r7+#iA0NpP{?4+}oUAWeT6?}T{ zR6R~nLObxjO0$|!@L`>`l~VRw+|HUMU#oq$9YE}kyUJ?w(Qahj2Ls1n&A=4DSSPsCl`>`9~>!Yb~S*fAY?t_%q6IO z_j1tF_{#MYP&}>C^YMg|myxTiTEfnG)4ymMVJ#8Tq${<1ay1p57W)6gj$mSs z=EcR`OO}`Of(*#5FY>?OlB#7J`-0+WCaOzXd3s zmf}moGqoAsnlN$b1Dh>?Ty9aq^6jF&Ix_`}M%MwemMSf62>B8fJIP%B$u)DChGCAQ zBFWj8=17&xPeVKL(lYZ>Hc_l}m*Vj#)R*LeRDefeCVZ|DiDRic|KJG;NPGe9>4L6D7Fo}uBZJSk5&9ORd#uko%iO87*l}Y^a-;wU?o^zl63UHWgTLpv=R}V zir0J)f5BnaaB0=y)p*-_R@inLq3b&RRyKHFEO2-8t;)|y3w$kRf3U{ygFp-q56dnZ zy{MY2EGM&X?Tk6meGuY;Nww0mE4ppVl{bJGX0(tOB>f?-lEQ&i@kvoDDeJ12+Rvr+ zXY&Zs-TE1xj1MHE@0q%l5p})wsj1SM(!_ngN&s z4bM}6RK|Z@xNdP}$#bx;__HMhp&Qj-1}Wlda0EF-mb5MIzASJGZy64tEzd^ytZ_1- z*)P-X$-n-Hy+pw#@%Iy%3=iWlGvI>(NM zM@H2h_eebj_#0G5AE5o|TiTJEi<)ewo?m;RSyol+qg%cFOB{qbnCoC_~*<4|yY#Qr_!a<@m9ld>n_k6|MfpEI3i;$c(zlbm+^5! z>yMtwiGg_$yh2kIQ3vXnLyT1WJ`PHf>sbAGwhSKt)2G-Lp7+z>t#fn-J69ZD*R zGa{t}WrmjEg3F{zF4$nlc5CYgRU12>J_U#O&d@bIBT9j3>pxNwwiDuz#5mG_P{#dl z?N1HJ(pa6%+DdQBaY#m5BlPbrXpRBi&3)W2xpUEOHS^$WhC~zjl~IW=vq?~tKMi<* zk~cIk5?VG)yWmrFX~;ezyfJ_Lm%#2;{Nk}IY4#B_O? z=lr$~nMhbIt=a{n$Y5~{eY#k*vkI{GDBeSMY>84p|L^37bB86VR?ss76>FgCnD-Yg zGCMq-?|q@?TN##Xzih0oVODr01&2z$nNFPmt_S%ppgPwrcEQYmzP~8S;xQGS+YpXl~y#>(fmnW+U(=^0|2;`dupb1^1VTU{RR8g zb}XA3-CdLe!sS=iS~DN^Tyiu|`j&^z@E&)XrP&FawHnux+C{am)ZCzBJR#+R-&K-LLf=0k1a~(G`Vq745rzK?q5L%1$>EKMPJ2 z|5cr=G>}4!I9-}lt!Gmce1AmV<5K)LfRVyn)RB^Mo5lS+?XszQcjdknbc=BS5xJ0fFVSp3YIz(M zYq)#%`cUXYsCM8_1cem=`xLSu*R~}B9{mxHRnV}J#oc92jqv9pQ^i^9%x1|>)sZdYqm6A5PmgIc!iyNVT>V&aOo-XY1`?WA zAA+?PDCx!9)!JaxYtJkW@Y(7Onz{dlVamnhi9hsZ*G>HW3ISygvbEZ{=*HI@jP)E_ zJwpR;E?8F!3Z0J%Y0nb-U6-?>1llTL29=XPV5n)x0325PjNcr4l?``+!Wd&vJEO*8 z>Xv{%JKj(h_C7@We`@+CiX_}9e}C8ZkY)#=Ti)bJa#4Sd#s(6={=26hD1FF;(mxTX z4=_pC(Cn~{`xLkN7!I!3_I9~4lf3+bhNNTA6Eu4q)l3x;dB0^m!5=IAfU`Oi41By^ zM_t`;;m!szM5;N1FzNwU(osO1tN?n%b)Bj(hg$jUz?gnf20rkuQi{YVK-wqBrZXW>Hp7{$FLo&QaS zxukff=N|7)2)TT-%0|r$#JZMBKa8OvU^$*t-m~_6OYx-vVe{Mbuq=o}5Ab4evNKZ7 zq=mW^GoTMVFs~f)_QCRhBjXs#`%IWYtNmS&JjUDETVV8D>e`(Q(8IOf?ZRV=tUi zjS%|BbrfdMTNqkGjy$O*Mijkw>0_UO$CGQv*^eE>|FIYu-ZF--gBEJ5sN%|2>oZB@ z11y{4(y{)ba@9vFzi*uH*Jua@?-&}7o~UnD$RD$O}`Yc{kMVh zk;AnpJw398?8)$g`uCt`mSNs%kW4t*uJelH2>PY=;{U(N-=!3O$k@qEcZxgxM-ptE_v;>yb6=O8d$qRg-Je#EB=`X%XaM}B)QmHQ!Q z#R)frJFAxfigOMW9wMU(Iqq~YGK$(I8g;zQbuuud!+%ceqOlib`N3u5Wh!65hF-j4 zYGLN{#uhkEws=W*tt2bGZm2y`V@0NKJ|n2LP_ivEZa&bi*{gKmqCBm!czK5u!kF^% z{9Jye#PDRQ2U13}_YH>0;N9m@U`>Ac>TQP0XG)m?F=TMzcVqJl zd*yxRy4?%SD{RD&djd{gyoq#W{eG=?LI$b4INN)ilSW<+;!)>i>_L^57yh0jF927L zzs48@WV}sJ=>Lm)P3NLuXPHT^Lqz!cL%Kn$htuWCzE?j^kkB@91Q2IyyH6l!t5i|Q};dsmUAUI&D@-HVdhwP7hKg$JJbq@6JoKmY& zKyN7`_UNSFyWf*dTnGmA50bunyYGP{H6^%mAHMU$``stE3AolfKik!NOoW#!x19Wh5{&VX3^u4Q96WeyNY=AXvcqzG~mlK$zJ`i)4*x;33- z#N!8a03DPq*Q|*> z7hQ!$o5An1K5c!=2(SAwA<&gkrxU+iAQ1@=l0+#oyR)!wf?bg#^t(c-1gqa|CVu66 zM}PEovo<+Lx;Y^vv@MSRP+0fRqJq|< zbFJ$9uTMA@B*CZ5j2R@PYwVqeUu#EKyog@q@O#DUaTA#`ok_FA6EhkMc-FRG33k~| z1@U@P>H~@{sDf(OwXRwdmWl?ku0k@@I-%6tAa`$<8bP{J$qVAl-M_ksnt*?u8V`gm-bHC z@G_U*vPsFDCdcpAlxJ#)>bw=rFw3FuP$y=DoZB;Bb=OeQ2jN}7j(=RZkdrnQ2CNGW zs8LAh$mh~mB6JIm(m$DUwrTC=N^Q@a&_SUJTyW=?qS*G!>Ls^w2rqSki>=E$8C-!r zyV!u}@1)A+ZMVPlS@!EAm%Q%!8PnyrG}G)rl2=)1k&eTS+onh2ziB+@e)c>XZTbEx(rJ4qg`1flxXMUip>sz)6%Gc3c0jEYn&t;Q2&-2>Nabi(i0vcSskI; zi-IXR39jj`fKdQC)15Uk9jPsh5t9)NoLp<8*so7BlujI^zs~Qd^Wg&y%wvGO_12X- zV0WEzV;uj|?=7^}1mC$q^jf zTM#hYgFEXHSDEQwEPvszdl^hb#(hWBSh75t^bJmDUq4H%dsjv^TmQ(9>r5yTaxa>D z*ZHhqjQ+2Tq11Jn`vZ%ZhzZyGYTlsMx`@o)T`ar4A;e1J0;s|y3x55ETKEO0R4A7W z%b)tBHO-t@^~D#c=Vh4&nsNdY&DF{^0mhyd;W^EqRyvgnte z;be-)>$))bZ5HIlr;=8|lB7j_sj6gKJOuN(jRfM%ovFQ9$rgOerZ1s6o~60)t)go` zbHp!ij>LUI81*k1Rr!?JYa{lXtkG+Mr=D*Wm+Xi6&-gYOuGhazw`KLcfQn! zI1svZ(@i(w`M#2Q)?#T2)Ddq4T{OAyN4EUSX7cCpmzg*yM!sD3t7-T|z5!;VR>!NV zs=e$BT&FPWhmDG*klDO>!0f%->_Q=b_o=PhQdSLA7s}x@#B3RvRaZdP<$x_$-e|eI z1HQZXKGQeTGvQ<3>ujNla;BEF!I zD~TQtxFK2}I=iv*8rrT$$nBPh*1q#usH9%x3m)G{%GUTZS-ceyTCu77!o7<_zAeHa z;S5B~yBDpfpsdC^+?;KSLm#V*-dl_jho=d z8(k@yq$g`nheq8YINO>oQ(v@oYke*9dQW{eadbveVKy6a%iRu6_oeMW##*>(yYpSYX4SubWHEPPu-E>o+R-Z) zd_PJ3WD(V%0f9Gd{L~Hq#S6R`rFtaX;zA7a$b1f;4(xzOCN{eo02vwZM={&9(Gtngn? zn1GD+FvL%p2$&#d4y>U*gxz=4*<|w@8i1!cxX%mIK+ba5oiqE4`L=q68>#RPZ*?RM z{9qCzww5g}bl;uS3dkzbhE$KqvN8`R28SNR@No!Qspj5!yGw3XUnZ5|9??->)~mtH zRM9?!^UMUrv?}~y?1Y7+Q(m(6>+hS?`>$iwe2{#_)P^WU)|L-0-+k(zQBQx+zx9XcfRr*{(_-G^B&OD1C zJBC0CCys|KpALM{;s zurZ?B7~c3WzM>)jDlp53>zkA-0(hVYCt9$Ik3I&oEs1qPpw1`-Z0i-dE#{)2h7{alc1qFWcxiN)4JfWr97OC^ zjCS>ovX(EH@%DR!8Vz3wPwlCCUZtO0FhK9Z8-Q8@1?t>%AaXUKS zR^fupte)PjlvQTs74y_*FI2u=>hcA4R8VgxsyjW@I}B*L z=i>D{PoIp+cO#?V<*jnVFNb~;g>IT}RBT&r21fG(7xeaEpi8NzDEA{GiQ(T^hny&Z zQ^4RzhWApPD2exwApn?Og{cbmaV`dJ3@%>JF-I5X*ES04;NQ>5goJ_sY=bc*+K?2d zFVot#6AYSBdEWEiT2oJlNR*!iq5<|xNXEV+=CtO%GKhFR<6dZpOrjSVSAc=x5O zqazw`g-U#F4nw3Xx1u}8qIZe5#6EPRugz)K~>R+itT$yT=>ZYY8S?6XmGS*fb zo-fw4K`8Faj;)rHg$c^{r8xGUe`6kT3{kTM>=lng9+S{WqVNNUJA{=??(j=0| zpI^*p&XBM?>i)3E|N1A7@a^mKgr?)%J7UKC(#3IRtkQ-fH@8l(YFk|ypamZ{YX732 z>*P7Q-D@p_z~k!6MIvyO-+vKkjzUb2uJd^P0|=&832vN<9g95ZHwn;Svg?UL9~+(7 z9|lF+uM&$Qi~IUUMef^fflAQjkg_H&9*%Sz&>)+BZH_f5OK+&V@p)K)ETklk5Gkv_ zGC&D|zoKPBlRpG;OOp9vWbq`KQ@fgTk!<6|JWG(f4>ons1W?prjd+9k{m>WP#eKkZ zB1zMzAjI;p2QGX*!MM?lfK%C#kH4$EL6i97JCJ{K2cPG?75#O3uher5 zO8-_9LgN+|awOmSM?M?J;bJ{{cl||~d|E{vuU_X!x*ALY!HZ{boVZ|`acI80toHy7 zhpcIkX2sTx@IPlhixra#{ z>qlascjS&>HWc$=5`YIzrIhDR##ht39@x>$@0h4WNLR)*Rw)0Ve)_20W;_B|GqoLRtj1U9Jh(x;a&8pD$e)N@% zp5SzaUuVPlglv+*@rC*Xx&jp0159;+{gt91i-Xv9DK~gBgKMR>`6rWk*_#;yXPlp5 zs2UC#_5UQ%uR~4k+f{dcLF&)ryyZg1P7RXNK2hr8EDr=)cQh4~fXH@Zl0Z@`aFLzR-(TWP6ZElPMK?I$h#N*VS&w zPYG#H>~StHD&r>hI4e+wuYJ1tHXis=qvnLJf$is7w z7QHnT2dFL5R(X4$B?fK*VO41d@gPL4`unHx*;?IQ9DH%B5yEX3n_T%n?VVRpR86<{ z2M|F)P|1=(k|0TPmMD@$at4u{L2}M0ISWIQAVG4@ASgMfArCOX07DRlAOkXI^SsZ^ z^Pc+7#amyUt215IRr_K>@7=Syd#(TaE%B!LcW#1dNjR@xumCJ@cX)w@a4+`PxI99sOg?4z@Ps(&*k zBB6kv>zSlboL>;g4gbP2aWqk-6fmAGN=rWG1@UOhjG6%*`T1MS9qa8SnP>9TLP-&C zs9e{sRf&97f72xfRlQwQV-+=uD zGMBz=Am_)~0^jwQig_)+G9YThP|q!YBC&zn7G!^Ir6CTUr0f1V5HIaRM5->J+l0IF z0p?tX920HH_1^ZpKfrsFz=imXqq@_jeg0UwD$;PoPvRs3v02-zx+{^2X~aQaEa^YufB$=-%OpdW_W-gX-5&tjyzu2Hne zQ0ymvQyjykb<`uA>7D5qacBVGY;R#^m>&jNC^pR`cZ>|{WA0W=kH^lBLfB2!PVU=; zmKw6V&)km%%A`)!A5FAqPUR!Ef;#0s4jY=fe0;1O;|%goGq&9wcqlt36z?kH_rzum zJB0Lp=)E~-rb3cS*XM}I3)?r9R8UY6+0#uKm#8G$p%j}$Gx{+Na3$7y;)?G?UT{D7 zrk|?Ri7e}?O&ZV9N3wpB&klY3iLbN2@?ZK8an_%S1V=v@aGgr*MZE0#mJ;(*{WjRN zh6a%SiWF205Y_-jO|v(zWEz62S=rUliH+95X?En2LkI6pp)An~_zhkNGA4no-`#kX z76)~w(|{tFBvyk%{H#-T_P3s#LdAH;oB;i))N~#A!h1 z2}B1yIHkQvn1EGq-whO2V=Z%4A%+}rnXWj$NW|85smOxsJiv#XAy+VDNgr3|2?(fUri?H)rmVBKSi_%rShdk~ zol?n5;1AcThd^VJL3sf+LrE8_;6Ww@(9|zS3I&@>x44z}p*WF3(sPS$@s8UQG#x7r z>es+dS^MqJ*+G74>1xf)a>(?UoE~EGZq!qY_LC{R#7PoHR1i!pW>fLK#pVxT?ZcD+ zDX6JNa8N*D++X=D**+L*Miv-s4-XbyB0U_J11EicK6<_Pv0NZSHnf?IlEP=fBK`UA zO6%SOk7-6^12aZkqLmvSP{}z%UOO)H$>@<^LDQ<(lzqDkUiN6VRVrcakXn!&Xm&cD zE~iu`^4u}|c;9VY&W^C}A?TOSA+RTtp+qLF3MuKi&}1Oz*5TXe*Yv7 ztC68(D55!~_D9i>UXG6sJbQ+2Xk7-K= zxv4~55aeYDW0m2VdOi6QRnLB;9<=TPOh_6p-{qi}*yij=7{ml!WZL08Ttd0>xng*x zThc$8NJ8hX}5b5lB*B^ql0PNfspQZcVv!%8v0&{9QzZm1MPTmTHG1uD1wdy`kP1iEIIB==F_js8CxZu2E7#-yB* zsC_TU>l#*sSWZu0jJFO{U!Qm2zk`{I6Dtf%oGu(J&IS2jN7Z?w)aE4E50sYz%PKSm zpJ{ZbYga4{na}ymptv8~1u=GIPy*`gCTsCge#y5Hx1p!r>2j{xlgRV<=5^*5ALbjf zGKp}q*Qi8VXwP#!lz(2lo?)R6S{IU-l%+N4TO9wrDjRrj-tW{wP=QiSk&DiBFxtLp zJJU`?0n5jDVohl3YX;GEjqJ7}bOE$w+d_Ehu`h1UU_^2eKGjY%*y|ZfpvRcXDKNh#SDe&L$LB;1C@&zZ*GQ{=k0WFee#pKbVk)x$%VN5(+Qo&NvJq_?OsV*W*X_6&U&|Sq{JxU5@7= zVz46Ix&&^1RHy+T3u#c5GZ()u0S6e|ks5Pc_jkdMVgz-%KSwM3T|cS=igsPj zwm@y*Re$M@7fdRsb@Mf6Shx+}%bEGHiN<} z>cMATx2Z9oY5&fTdHEF78$JJ+a|Q#4S=meMq+G+`c<~j34MrppYWu5txHR>}qo?vv zfg!#c<1T*1l1Kg(Gc9*j5ZRGMQwW3)?<}M)3+qIF{w4YO;Du265##m7u`ZqvN+OT7 zd(Oq|r+aH5d=d82!TT3y7z?2a$OT0!Xku1zbopA+Uv13&cgZ3to)1~}CgAB_eN_t( z!R(>GP|Gip_j?-qmXdPQu(|Qp--eNuB;yeBeycc#hdlM};$p+cc*4}m(Wu0x$K=1P z&Mr^|XehoTa@4%9;!}#r8b#_WmLUo`2PlM76t&E&$dEHXyhW?YhYEUE95BVBRRv;l z^Flu{ftm#wtm@4iiBFOHizzZm3cjFpJ1RX^sC`FueeG%<->el{aY@@o@HJoG!-MbY zhwB}WtF%(1Ht8%P%+G|n{kqJMqlV#6KpN@KO%IDCFWp%0-`zIu9&y=Z?|T~CRVq*1 z)Ei-J<8h>qwVGhfDq&i^AkT09Cd-wKZV!;SQO8ccrG*NJZrrQ+##p)LpCRl#wORb> zBq1Fxx;N0PGENgoN}g0`C=48Shwd^sPH5BbnGumid&h8#HbQ4tlBxr}e`}~JhWmM) zsz-vwTMuZwi4z30PCi?54(T8DdlZCt1*yD^xJ#RqbK$zNrZECAjyZ?ocpk;WI;o!D zs~Zc{>-XF{>%e_w@Fs-OU?+g{Vk53$@G0x&6}8KUJEkYYQp~0jA2rDDb1#qgI)T!9 zt0?u8iVFuBXg3pcK{3XD#}+mILFe3#+1`C@H4c@)(gZ$9h^cuSar8x{Qs;!#Ji~+; zY^o4TwQl0kML{%9;o6BLhq2_LYlJ^W5f(9+?2b~p$n$n72XvwK5My=ul5iKoUST)I zNF$H9_BB@{+yedK=la9rA&dy|IseqJugleJ?TDmsE3DH_V}wE97OGcPX6iJD&vDT^!G%w$aecfrM1mmez z8=G3xad=Ny;S0l+n^vbmtt>2(mUBi{*(JJCzp$P+t0M1!Xrx-1(yUDz9Xqm>5TVWa zn66oc0JNawE8Vxdl*G%`l76N_7kcqAEig`a#kAV^=YV55cz*=VO~2&Lu2@UmOVy|g z&pC=>;SO9%5DgXDIhbs@`2kT98AYqkIL+67vlS(ueDea)Neb*37302>x^amWxi3T@ zJPVtNjG17vW=f~_K@8>(fol@5No@m1Gfp9NR>{%sIcidK07j|dzeZv6Oz!a(7ofn= zq)w^h9v~hW7b1?ry1YOvE#(QuPRIYKK+b20zaLm|l&J|CWW_2dfB468a{$cP%?R1$ zvKO-ab53^sp=>y&KFofoN4V(+8zpqcQf_Wdk*e4m52Q)jUB!~U& zff5N3!vncHl-1t2)_pjs@B>L8;N|FnNTpmi$ z*Bmgbl};iDpW^jpqX>_}QK0tEvg@la9sfB7u>m4xzh$tP4UCp+uyFkLG_7@!VJ7p<(T~dF}YlO;{W3M zgn2~o9fp?+Z@GSu6?3C7>Y-1`wC>a{a2xm5)|m3^FZfw@mr!zue}`L9C)ZFMLdh^5 zOw>$Zu^#Ugbm5NRXX-=am~#VBgcVETg_7N3VmdaID?`ye3yNH(DyrQ;J@;o%svkr! zl=eIKrDS`ud4xs!y|FDYSUlFtQH!&g^NSakm>jAhBP+Kmq>8IgR%w*=KpvDdRCL`T zMwqMqYXueP%T9N-PQT^*-7cUUvsQL6wPloMIy>CGRAVwOou)z6&e>M;Q+-F!gJ8d_ zMK_l#`_5z3-5YkH5U#I0pu9Xjf`IU14 znsKe?j;h;PY+PcnET)qzm(R&fP;ZD_C#Rrbf*>B#Y-tuiGFR7JLYAvyX4C<+9>zBD z6TUn&fSZrc{NjEv`7`_!SSph{n*ZUNHp75}LiR;HTz#&xJ*k8|hMzZb+kJDk#q&ZE z@e_vM>*=SQ>z3KcA2n#?1M4~6#HQ(ewGZgD^1|}k!D|Db#|IitmOJzoH&{=KW$q7? zzS#oTla0R+V~Lm`^+?yjLuje{R+qXa@0E+BXjXNew;Gi2IkY-1_`D&Y>mF+mrA0e- zIB|4cp8ybf2oHkaVUL*9;)Tu{bI2B8>uWJj=WB=E?1NL8t6LpZ{Xx3#gIQgc1G55h2$K3cEx`&UPS&LEi-OfTp!lCmV0!_NSu06V6Ey}UVO;m6p4h@XSuf?kyk z6UpnKz#ACtHZU%`%=xH?4TCCrh*P9RM)Wmr*%{@;L$^sB+L1;#=o3z=B^08H77sM* zF!WbSPGaP$H(VUDN>a`dCKwdEZ(`K0v%QmX)OCcvfxchzqwlAK12OwX|`WS1tb_8?ktTO`kbLECFRyqLRtv;ou z5~jdis>OnM4H{G*Lcdn~j{qgM#GlO<#oQ+DG~^ToHb#$*Zsq2#G@naCg`J*@rgK+u zM5V}e8a~V|;9Qi2zTD0gzmmBScWT+%t}*UWnjQ80a|;j?4nD53nRdrF&2pd9TjH~w zDbZ{kfnY?|*~F^MzhM$wfXuN942rO+RUl!Tse3g!z(8!!}emzvnG;{%RpAF-1;? zPx#uR^V=mrod>8Tcae)D#Gz%OY$_d^onX_uhe*Gqte zp1UxA-e$Y_sP=q;<4>`^2#J{=O@{Q)fY0@L+tG*ut^fXe@>AzqEmSROs*uUznVkP| zq&LNiAgUdJtBH6!sC*kX01&T3670M`bxiynZfpgUHy))mY?Y}z0nZ+Mg}B0AmI{Bd zw;*ZFIM3d(9gHSZ|1h*8^m*KN+S4-Qg?qN%Sl!Ntq8zd7MBXxgWl@&Rbv6FnSWoo_ z*-dC=`w{TF0=58$t^+`1OX7fkJgu|gnR6^Wni$-Sd_EdV)h>)iHSY@mzxPOWaC*J$U5ITS-d-~_F% zphMdrCIi&VhVn!~`UPkI*JpCe-ZmX#NH zk157Fs5S1ubAG{_x^?43$xsM|U1;-#A{Z;0ALkd<9d4?i*I78QpP9nz>ty-_pv7l6 zmAU-Bal4KE5{H++yc9IMr0@uFNaZr}8zp?l#V=Y)nA@48Yhe}-dh*&fKC!3W$wHqD zSAF%5j8nbonckoGJ#@b8>FN&t?`_XHPe4zyT5LXnyoxtP@ zrxbvWP*tNlzp5SOScX2Bix(R>X(x;eoCns)g}Ae2nK)Y@i{$WP0PHAmv3e1-v^-Jd z)*qs>?Da?tC{I2D)L7q{GWHk@w}kp%R37P&?-5er{#(;67kW5X96gJu{M~yOZ17XB zRhO)pbx(6|BmAzZ7jNZlCqYtlzxx+MS{Pn1)<4_X07eh-?Fq!P$6SrLVFRc#0(rvQ zz^c()J>W{mT1vX0LrKHoOh<-8aJbs545UZt^!37y1Sy@C8Fakq-PWYmqyh*)#A4S` z=7ujr2C}J_oA(@gFogQTj?68afMCmkbf# za_z+eyUylfbhokgY51IjY!*MPgk0USf+${WbHcT*P0%Qv@8SUuOSc9XQ5iCX!a_kk z(al8v?SFEIU`8CL2UUOQ-OdVmXzv((vQ%+X_qut{^_13I9fffgu&tmdiV zzt6kdYh7HSU%BY({_87vZg&49P_EjH-rB|b-R-R47YfQLco}+kw&dB&BcX>v2?HAxZi}MS z1KYuvGYerd$GiV#B7nOciDl-mPc6Y^+i(ZGlz%RdxbP-*2P{l8<*kLG06GA{_5ZLi z|Fk#%$(3dnP&q3nrs!|*H70j^6w|u&?QQ%2+kXB<6x02mC-;9cfPcQ{|AzA)J-7em br-P>TN3%)R8|deNfS012s%)jSX~=&8wm1ov literal 0 HcmV?d00001 From f111e3303c7d62a1722df205a395621367f9b0a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20S=C3=A1nchez-Mariscal?= Date: Mon, 12 Sep 2022 18:24:43 +0200 Subject: [PATCH 19/51] Cross reference to Micronaut Object Storage (#1462) --- src/main/docs/guide/sdkv2/s3.adoc | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/docs/guide/sdkv2/s3.adoc b/src/main/docs/guide/sdkv2/s3.adoc index 85fc1499fc..6d10558b15 100644 --- a/src/main/docs/guide/sdkv2/s3.adoc +++ b/src/main/docs/guide/sdkv2/s3.adoc @@ -1,4 +1,12 @@ -To use an S3 client, add the following dependency: +Micronaut provides a high-level, uniform object storage API that works across the major cloud providers: https://micronaut-projects.github.io/micronaut-object-storage/latest/guide/[Micronaut Object Storage]. + +To get started, select the `object-storage-aws` feature in https://micronaut.io/launch?features=object-storage-aws[Micronaut Launch], or add the following dependency: + +dependency:io.micronaut.objectstorage:micronaut-object-storage-aws[] + +For more information, check the https://micronaut-projects.github.io/micronaut-object-storage/latest/guide/#aws[Micronaut Object Storage AWS support] documentation. + +If you still need the low-level AWS S3 client, add the following dependency: dependency:s3[groupId="software.amazon.awssdk"] @@ -16,4 +24,4 @@ The HTTP client, credentials and region will be configured as per described in t Additionally, this service accepts the following configuration properties: -include::{includedir}configurationProperties/io.micronaut.aws.sdk.v2.service.s3.S3ConfigurationProperties.adoc[] \ No newline at end of file +include::{includedir}configurationProperties/io.micronaut.aws.sdk.v2.service.s3.S3ConfigurationProperties.adoc[] From 1f1f0ffd17d8a0a3e997fbb3f882646d957f4764 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 13 Sep 2022 12:31:16 +0200 Subject: [PATCH 20/51] fix(deps): update aws-java-sdk monorepo to v1.12.301 (#1458) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e93189dcdc..ff62aba253 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,7 @@ fileupload = '0.0.5' jetty = '9.4.48.v20220622' logback-json-classic = '0.1.5' managed-alexa-ask-sdk = "2.44.0" -managed-aws-java-sdk-v1 = '1.12.296' +managed-aws-java-sdk-v1 = '1.12.301' managed-aws-java-sdk-v2 = '2.17.271' managed-aws-lambda = '1.2.1' managed-aws-lambda-events = '3.11.0' From a536121fdfb1f2d1ce71c88335b3a72d3f6011bf Mon Sep 17 00:00:00 2001 From: Vladimir Orany Date: Tue, 13 Sep 2022 12:36:18 +0200 Subject: [PATCH 21/51] feat: New AfterExecutionEvent for AWS Lambda (#1433) Co-authored-by: Sergio del Amo --- .../function/aws/MicronautRequestHandler.java | 20 +- .../aws/MicronautRequestStreamHandler.java | 19 +- .../aws/event/AfterExecutionEvent.java | 99 ++++++++++ .../function/aws/event/package-info.java | 22 +++ .../aws/AfterExecutionEventSpec.groovy | 178 ++++++++++++++++++ .../guide/lambda/afterExecutionEvent.adoc | 3 + src/main/docs/guide/toc.yml | 1 + 7 files changed, 340 insertions(+), 2 deletions(-) create mode 100644 function-aws/src/main/java/io/micronaut/function/aws/event/AfterExecutionEvent.java create mode 100644 function-aws/src/main/java/io/micronaut/function/aws/event/package-info.java create mode 100644 function-aws/src/test/groovy/io/micronaut/function/aws/AfterExecutionEventSpec.groovy create mode 100644 src/main/docs/guide/lambda/afterExecutionEvent.adoc diff --git a/function-aws/src/main/java/io/micronaut/function/aws/MicronautRequestHandler.java b/function-aws/src/main/java/io/micronaut/function/aws/MicronautRequestHandler.java index 3301a43e65..c308d3602a 100644 --- a/function-aws/src/main/java/io/micronaut/function/aws/MicronautRequestHandler.java +++ b/function-aws/src/main/java/io/micronaut/function/aws/MicronautRequestHandler.java @@ -16,6 +16,7 @@ package io.micronaut.function.aws; import com.amazonaws.services.lambda.runtime.*; +import io.micronaut.context.event.ApplicationEventPublisher; import io.micronaut.core.annotation.NonNull; import io.micronaut.context.ApplicationContext; import io.micronaut.context.ApplicationContextBuilder; @@ -24,6 +25,7 @@ import io.micronaut.core.convert.ConversionError; import io.micronaut.core.reflect.GenericTypeUtils; import io.micronaut.core.util.ArrayUtils; +import io.micronaut.function.aws.event.AfterExecutionEvent; import io.micronaut.function.executor.AbstractFunctionExecutor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -95,6 +97,8 @@ public abstract class MicronautRequestHandler extends AbstractFunctionExec @SuppressWarnings("unchecked") private final Class inputType = initTypeArgument(); + private ApplicationEventPublisher eventPublisher; + /** * Default constructor; will initialize a suitable {@link ApplicationContext} for * Lambda deployment. @@ -143,7 +147,14 @@ public final O handleRequest(I input, Context context) { if (!inputType.isInstance(input)) { input = convertInput(input); } - return this.execute(input); + try { + O output = this.execute(input); + resolveAfterExecutionPublisher().publishEvent(AfterExecutionEvent.success(context, output)); + return output; + } catch (Throwable re) { + resolveAfterExecutionPublisher().publishEvent(AfterExecutionEvent.failure(context, re)); + throw re; + } } /** @@ -241,4 +252,11 @@ private Class initTypeArgument() { return Object.class; } } + + private ApplicationEventPublisher resolveAfterExecutionPublisher() { + if (eventPublisher == null) { + eventPublisher = applicationContext.getEventPublisher(AfterExecutionEvent.class); + } + return eventPublisher; + } } diff --git a/function-aws/src/main/java/io/micronaut/function/aws/MicronautRequestStreamHandler.java b/function-aws/src/main/java/io/micronaut/function/aws/MicronautRequestStreamHandler.java index 116567a39c..91c52cf9f9 100644 --- a/function-aws/src/main/java/io/micronaut/function/aws/MicronautRequestStreamHandler.java +++ b/function-aws/src/main/java/io/micronaut/function/aws/MicronautRequestStreamHandler.java @@ -20,7 +20,9 @@ import io.micronaut.context.ApplicationContext; import io.micronaut.context.ApplicationContextBuilder; import io.micronaut.context.env.Environment; +import io.micronaut.context.event.ApplicationEventPublisher; import io.micronaut.core.annotation.Nullable; +import io.micronaut.function.aws.event.AfterExecutionEvent; import io.micronaut.function.executor.StreamFunctionExecutor; import io.micronaut.core.annotation.NonNull; import org.slf4j.Logger; @@ -43,6 +45,8 @@ public class MicronautRequestStreamHandler extends StreamFunctionExecutor eventPublisher; + @Nullable private String ctxFunctionName; @@ -76,7 +80,13 @@ public void handleRequest(InputStream input, OutputStream output, Context contex this.ctxFunctionName = context.getFunctionName(); } HandlerUtils.configureWithContext(this, context); - execute(input, output, context); + try { + execute(input, output, context); + resolveAfterExecutionPublisher().publishEvent(AfterExecutionEvent.success(context, null)); + } catch (Throwable e) { + resolveAfterExecutionPublisher().publishEvent(AfterExecutionEvent.failure(context, e)); + throw e; + } } @Override @@ -96,6 +106,13 @@ protected String resolveFunctionName(Environment env) { return (functionName != null) ? functionName : ctxFunctionName; } + private ApplicationEventPublisher resolveAfterExecutionPublisher() { + if (eventPublisher == null) { + eventPublisher = applicationContext.getEventPublisher(AfterExecutionEvent.class); + } + return eventPublisher; + } + @Override public void close() { // Don't close the application context diff --git a/function-aws/src/main/java/io/micronaut/function/aws/event/AfterExecutionEvent.java b/function-aws/src/main/java/io/micronaut/function/aws/event/AfterExecutionEvent.java new file mode 100644 index 0000000000..42be5321a2 --- /dev/null +++ b/function-aws/src/main/java/io/micronaut/function/aws/event/AfterExecutionEvent.java @@ -0,0 +1,99 @@ +/* + * Copyright 2017-2020 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.function.aws.event; + +import com.amazonaws.services.lambda.runtime.Context; +import io.micronaut.core.annotation.Nullable; + +/** + * This event is published after the execution of {@link io.micronaut.function.aws.MicronautRequestHandler#execute(Object)} + * and {@link io.micronaut.function.aws.MicronautRequestStreamHandler#execute(java.io.InputStream, java.io.OutputStream)} methods to allow + * performing actions before the Lambda function run is finished and the JVM is hibernated. + *

+ * This event must be processed synchronously to guarantee it has been processed before the Lambda funciton is hibernated. + * + * @author Vladimir Orany + * @since 3.9.0 + */ +public final class AfterExecutionEvent { + + @Nullable + private final Context context; + + @Nullable + private final Throwable exception; + @Nullable + private final Object output; + + private AfterExecutionEvent(@Nullable Context context, @Nullable Object output, @Nullable Throwable exception) { + this.context = context; + this.output = output; + this.exception = exception; + } + + /** + * Creates a new {@link AfterExecutionEvent} with an optional result of the execution. + * + * @param context AWS Lambda context + * @param output an optional result of the exectuion + * @return a new {@link AfterExecutionEvent} with an optional result of the execution + */ + public static AfterExecutionEvent success(@Nullable Context context, @Nullable Object output) { + return new AfterExecutionEvent(context, output, null); + } + + /** + * Creates a new {@link AfterExecutionEvent} with an exception been thrown. + * + * @param context AWS Lambda context + * @param exception the exception which has been thrown during the execution + * @return a new {@link AfterExecutionEvent} with an exception been thrown. + */ + public static AfterExecutionEvent failure(@Nullable Context context, Throwable exception) { + return new AfterExecutionEvent(context, null, exception); + } + + /** + * @return true if there were no exception thrown + */ + public boolean isSuccess() { + return exception == null; + } + + /** + * @return the optional result of the execution + */ + @Nullable + public Object getOutput() { + return output; + } + + /** + * @return the optional exception which has been thrown + */ + @Nullable + public Throwable getException() { + return exception; + } + + /** + * @return the optional Lambda context + */ + @Nullable + public Context getContext() { + return context; + } +} diff --git a/function-aws/src/main/java/io/micronaut/function/aws/event/package-info.java b/function-aws/src/main/java/io/micronaut/function/aws/event/package-info.java new file mode 100644 index 0000000000..be196a7c54 --- /dev/null +++ b/function-aws/src/main/java/io/micronaut/function/aws/event/package-info.java @@ -0,0 +1,22 @@ +/* + * Copyright 2017-2020 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://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. + */ +/** + * Event classes to support AWS Lambda lifecycle. + * + * @author Vladimir Orany + * @since 3.8.0 + */ +package io.micronaut.function.aws.event; diff --git a/function-aws/src/test/groovy/io/micronaut/function/aws/AfterExecutionEventSpec.groovy b/function-aws/src/test/groovy/io/micronaut/function/aws/AfterExecutionEventSpec.groovy new file mode 100644 index 0000000000..a90539e826 --- /dev/null +++ b/function-aws/src/test/groovy/io/micronaut/function/aws/AfterExecutionEventSpec.groovy @@ -0,0 +1,178 @@ +package io.micronaut.function.aws + +import com.amazonaws.services.lambda.runtime.Context +import groovy.transform.CompileStatic +import groovy.transform.InheritConstructors +import io.micronaut.context.ApplicationContext +import io.micronaut.context.ApplicationContextBuilder +import io.micronaut.context.annotation.Requires +import io.micronaut.context.env.Environment +import io.micronaut.context.event.ApplicationEventListener +import io.micronaut.core.annotation.NonNull +import io.micronaut.function.FunctionBean +import io.micronaut.function.aws.event.AfterExecutionEvent +import jakarta.inject.Singleton +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import spock.lang.Specification +import java.util.function.Function + +class AfterExecutionEventSpec extends Specification { + void 'micronaut request handler with success without AfterExecutionEvent listener'() { + given: + Handler handler = new Handler() + ApplicationContext context = handler.applicationContext + + when: + String output = handler.handleRequest('hello', Mock(Context)) + then: + output == 'olleh' + !context.containsBean(AfterExecutionEventListener) + + cleanup: + handler.close() + } + + void 'micronaut request handler with success'() { + given: + Handler handler = new Handler(builderWithSpecName("AfterExecutionEventSpec")) + ApplicationContext context = handler.applicationContext + + when: + String output = handler.handleRequest('hello', Mock(Context)) + then: + output == 'olleh' + context.containsBean(AfterExecutionEventListener) + context.getBean(AfterExecutionEventListener).lastEvent + context.getBean(AfterExecutionEventListener).lastEvent.success + context.getBean(AfterExecutionEventListener).lastEvent.output == output + + cleanup: + handler.close() + } + + void 'micronaut request handler with failure'() { + given: + Handler handler = new Handler(builderWithSpecName("AfterExecutionEventSpec")) + ApplicationContext context = handler.applicationContext + + when: + handler.handleRequest('foo', Mock(Context)) + then: + thrown(IllegalArgumentException) + + context.containsBean(AfterExecutionEventListener) + context.getBean(AfterExecutionEventListener).lastEvent + !context.getBean(AfterExecutionEventListener).lastEvent.success + context.getBean(AfterExecutionEventListener).lastEvent.exception instanceof IllegalArgumentException + + cleanup: + handler.close() + } + + void 'micronaut steam request handler with success'() { + given: + StreamHandler handler = new StreamHandler() + ApplicationContext context = handler.applicationContext + + when: + ByteArrayOutputStream outputStream = new ByteArrayOutputStream() + handler.handleRequest(new ByteArrayInputStream('hello'.bytes), outputStream, Mock(Context)) + then: + outputStream.toString() == 'olleh' + context.containsBean(AfterExecutionEventListener) + context.getBean(AfterExecutionEventListener).lastEvent + context.getBean(AfterExecutionEventListener).lastEvent.success + // no output capturing for streaming implementation + context.getBean(AfterExecutionEventListener).lastEvent.output == null + + cleanup: + handler.close() + } + + void 'micronaut steam request handler with failure'() { + given: + StreamHandler handler = new StreamHandler() + ApplicationContext context = handler.applicationContext + + when: + ByteArrayOutputStream outputStream = new ByteArrayOutputStream() + handler.handleRequest(new ByteArrayInputStream('foo'.bytes), outputStream, Mock(Context)) + + then: + thrown(IllegalArgumentException) + context.containsBean(AfterExecutionEventListener) + context.getBean(AfterExecutionEventListener).lastEvent + !context.getBean(AfterExecutionEventListener).lastEvent.success + context.getBean(AfterExecutionEventListener).lastEvent.exception instanceof IllegalArgumentException + + cleanup: + handler.close() + } + + @NonNull + private static ApplicationContextBuilder builderWithSpecName(@NonNull String specName) { + Map properties = Collections.singletonMap("spec.name", specName) + ApplicationContextBuilder contextBuilder = new LambdaApplicationContextBuilder() + contextBuilder.properties(properties) + contextBuilder + } + + @Requires(property = "spec.name", value = "AfterExecutionEventSpec") + @Singleton + @CompileStatic + static class AfterExecutionEventListener implements ApplicationEventListener { + + AfterExecutionEvent lastEvent + + @Override + void onApplicationEvent(AfterExecutionEvent event) { + lastEvent = event + } + + } + + @Requires(property = "spec.name", value = "AfterExecutionEventSpec") + @FunctionBean("afterExecutionFun") + static class EventLogger implements Function { + private static final Logger LOG = LoggerFactory.getLogger(EventLogger.class); + @Override + String apply(String input) { + testScenario().apply(input) + } + } + + @InheritConstructors + class Handler extends MicronautRequestHandler { + @Override + String execute(String input) { + testScenario().apply(input) + } + } + + @InheritConstructors + class StreamHandler extends MicronautRequestStreamHandler { + + @Override + protected String resolveFunctionName(Environment env) { + return "afterExecutionFun" + } + + @Override + protected ApplicationContextBuilder newApplicationContextBuilder() { + return super.newApplicationContextBuilder().properties(Collections.singletonMap("spec.name", "AfterExecutionEventSpec")) + } + } + + private static Function testScenario() { + return new Function() { + @Override + String apply(String input) { + if (input == 'foo') { + throw new IllegalArgumentException('No foo allowed!') + } + return input.reverse() + } + } + } +} diff --git a/src/main/docs/guide/lambda/afterExecutionEvent.adoc b/src/main/docs/guide/lambda/afterExecutionEvent.adoc new file mode 100644 index 0000000000..d3cad98899 --- /dev/null +++ b/src/main/docs/guide/lambda/afterExecutionEvent.adoc @@ -0,0 +1,3 @@ +AWS Lambda runtime will reuse the instances of your handler classes but the Java Virtual Machine will be suspended between the invocation. Every call on a single instance of your handler will reuse the same application context. If you need to perform actions before the JVM gets suspended then you can create **synchronous** handler for `AfterExecutionEvent` event which is published right before the execution of the method `handleRequest` in `MicronautRequestHandler` and `MicronautRequestStreamHandler` is finished. + +To subscribe to `AfterExecutionEvent` create a `@Singleton` class which implements `ApplicationEventListener`. Learn more about https://docs.micronaut.io/latest/guide/#contextEvents[Context Events]. diff --git a/src/main/docs/guide/toc.yml b/src/main/docs/guide/toc.yml index ed23508a97..d4ea9ae8e3 100644 --- a/src/main/docs/guide/toc.yml +++ b/src/main/docs/guide/toc.yml @@ -47,6 +47,7 @@ lambda: lambdatriggers: Lambda Triggers lambdacontext: Lambda Context requestHandlers: Lambda Handlers + afterExecutionEvent: AfterExecutionEvent coldstartups: Cold Startups customRuntimes: GraalVM and AWS Custom runtimes mdc: MDC Logging From 563584adb97d4440ee728d331cc5c267a685f6c0 Mon Sep 17 00:00:00 2001 From: micronaut-build Date: Thu, 15 Sep 2022 05:37:25 +0000 Subject: [PATCH 22/51] [skip ci] Release v3.9.0 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 446aa3be61..0d1ca5c550 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -projectVersion=3.9.0-SNAPSHOT +projectVersion=3.9.0 projectGroup=io.micronaut.aws micronautDocsVersion=2.0.0 From 7a800f10bbf177d0a9df3018e96fb8174e9221a6 Mon Sep 17 00:00:00 2001 From: micronaut-build Date: Thu, 15 Sep 2022 05:43:25 +0000 Subject: [PATCH 23/51] Back to 3.9.1-SNAPSHOT --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 0d1ca5c550..10099c2e16 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -projectVersion=3.9.0 +projectVersion=3.9.1-SNAPSHOT projectGroup=io.micronaut.aws micronautDocsVersion=2.0.0 From 5cc9d7c2c4fbec0d51f9adf56d935b3a44925094 Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Fri, 16 Sep 2022 13:21:06 +0200 Subject: [PATCH 24/51] bug: add missing package-info-java (#1464) * bug: add missing package-info-java * add test --- .../service/cloudwatchlogs/package-info.java | 29 +++++++++++++++++ settings.gradle.kts | 1 + test-suite-aws-sdk-v2/build.gradle.kts | 31 +++++++++++++++++++ .../CloudwatchLogsClientFactoryTest.java | 19 ++++++++++++ 4 files changed, 80 insertions(+) create mode 100644 aws-sdk-v2/src/main/java/io/micronaut/aws/sdk/v2/service/cloudwatchlogs/package-info.java create mode 100644 test-suite-aws-sdk-v2/build.gradle.kts create mode 100644 test-suite-aws-sdk-v2/src/test/java/io/micronaut/aws/sdk/v2/service/cloudwatch/CloudwatchLogsClientFactoryTest.java diff --git a/aws-sdk-v2/src/main/java/io/micronaut/aws/sdk/v2/service/cloudwatchlogs/package-info.java b/aws-sdk-v2/src/main/java/io/micronaut/aws/sdk/v2/service/cloudwatchlogs/package-info.java new file mode 100644 index 0000000000..877f397761 --- /dev/null +++ b/aws-sdk-v2/src/main/java/io/micronaut/aws/sdk/v2/service/cloudwatchlogs/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright 2017-2020 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://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. + */ +/** + * DynamoDB client factory. + * + * @author Álvaro Sánchez-Mariscal + * @since 2.0.0 + */ +@Requires(classes = {CloudWatchLogsClient.class, CloudWatchLogsAsyncClient.class}) +@Configuration +package io.micronaut.aws.sdk.v2.service.cloudwatchlogs; + +import io.micronaut.context.annotation.Configuration; +import io.micronaut.context.annotation.Requires; +import software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsAsyncClient; +import software.amazon.awssdk.services.cloudwatchlogs.CloudWatchLogsClient; diff --git a/settings.gradle.kts b/settings.gradle.kts index c0e72920e7..5893d221f1 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -31,6 +31,7 @@ include("aws-cdk") include("aws-cloudwatch-logging") include("function-aws-test") include("test-suite") +include("test-suite-aws-sdk-v2") include("test-suite-groovy") include("test-suite-kotlin") diff --git a/test-suite-aws-sdk-v2/build.gradle.kts b/test-suite-aws-sdk-v2/build.gradle.kts new file mode 100644 index 0000000000..2edc3e0367 --- /dev/null +++ b/test-suite-aws-sdk-v2/build.gradle.kts @@ -0,0 +1,31 @@ +plugins { + id("java-library") +} + +repositories { + mavenCentral() +} + +val micronautVersion: String by project + +dependencies { + testAnnotationProcessor(platform("io.micronaut:micronaut-bom:$micronautVersion")) + testAnnotationProcessor("io.micronaut:micronaut-inject-java") + + testImplementation(platform("io.micronaut:micronaut-bom:$micronautVersion")) + testImplementation("org.junit.jupiter:junit-jupiter-api") + testImplementation("io.micronaut.test:micronaut-test-junit5") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") + + testImplementation(project(":aws-sdk-v2")) +} +tasks { + named("test", Test::class) { + useJUnitPlatform() + } +} + +java { + sourceCompatibility = JavaVersion.toVersion("1.8") + targetCompatibility = JavaVersion.toVersion("1.8") +} diff --git a/test-suite-aws-sdk-v2/src/test/java/io/micronaut/aws/sdk/v2/service/cloudwatch/CloudwatchLogsClientFactoryTest.java b/test-suite-aws-sdk-v2/src/test/java/io/micronaut/aws/sdk/v2/service/cloudwatch/CloudwatchLogsClientFactoryTest.java new file mode 100644 index 0000000000..a83f2ab4da --- /dev/null +++ b/test-suite-aws-sdk-v2/src/test/java/io/micronaut/aws/sdk/v2/service/cloudwatch/CloudwatchLogsClientFactoryTest.java @@ -0,0 +1,19 @@ +package io.micronaut.aws.sdk.v2.service.cloudwatch; + +import io.micronaut.aws.sdk.v2.service.cloudwatchlogs.CloudwatchLogsClientFactory; +import io.micronaut.context.BeanContext; +import io.micronaut.test.extensions.junit5.annotation.MicronautTest; +import jakarta.inject.Inject; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; + +@MicronautTest(startApplication = false) +class CloudwatchLogsClientFactoryTest { + @Inject + BeanContext beanContext; + @Test + void beanOfTypeCloudwatchLogsClientFactoryDoesNotExists() { + assertFalse(beanContext.containsBean(CloudwatchLogsClientFactory.class)); + } +} From 44c5aaa29f699e6510fd846b6640aa7ca72d2fab Mon Sep 17 00:00:00 2001 From: micronaut-build Date: Fri, 16 Sep 2022 11:31:15 +0000 Subject: [PATCH 25/51] [skip ci] Release v3.9.1 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 10099c2e16..1b174edf42 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -projectVersion=3.9.1-SNAPSHOT +projectVersion=3.9.1 projectGroup=io.micronaut.aws micronautDocsVersion=2.0.0 From 78c8155597df701dedbfa2e0bf5aadd60e7584eb Mon Sep 17 00:00:00 2001 From: micronaut-build Date: Fri, 16 Sep 2022 11:35:54 +0000 Subject: [PATCH 26/51] Back to 3.9.2-SNAPSHOT --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 1b174edf42..4df7df1fba 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -projectVersion=3.9.1 +projectVersion=3.9.2-SNAPSHOT projectGroup=io.micronaut.aws micronautDocsVersion=2.0.0 From 58712ea60b93b45fc6cf11e7d3be305181ead6ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20S=C3=A1nchez-Mariscal?= Date: Fri, 16 Sep 2022 17:09:30 +0200 Subject: [PATCH 27/51] Add necessary GraalVM configuration for the Apache HTTP client (#1465) --- .../micronaut-aws-sdk-v2/reflect-config.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 aws-sdk-v2/src/main/resources/META-INF/native-image/io.micronaut.aws/micronaut-aws-sdk-v2/reflect-config.json diff --git a/aws-sdk-v2/src/main/resources/META-INF/native-image/io.micronaut.aws/micronaut-aws-sdk-v2/reflect-config.json b/aws-sdk-v2/src/main/resources/META-INF/native-image/io.micronaut.aws/micronaut-aws-sdk-v2/reflect-config.json new file mode 100644 index 0000000000..0a8a692723 --- /dev/null +++ b/aws-sdk-v2/src/main/resources/META-INF/native-image/io.micronaut.aws/micronaut-aws-sdk-v2/reflect-config.json @@ -0,0 +1,17 @@ +[ + { + "name":"org.apache.commons.logging.impl.Jdk14Logger", + "methods":[{"name":"","parameterTypes":["java.lang.String"] }] + }, + { + "name":"org.apache.commons.logging.impl.Log4JLogger" + }, + { + "name":"org.apache.commons.logging.impl.LogFactoryImpl", + "methods":[{"name":"","parameterTypes":[] }] + }, + { + "name":"org.apache.commons.logging.impl.WeakHashtable", + "methods":[{"name":"","parameterTypes":[] }] + } +] From d4b4cce92138dca5031fa91f525d27e5fdd1069c Mon Sep 17 00:00:00 2001 From: micronaut-build Date: Fri, 16 Sep 2022 15:45:19 +0000 Subject: [PATCH 28/51] [skip ci] Release v3.9.2 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 4df7df1fba..c8bbb6d387 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -projectVersion=3.9.2-SNAPSHOT +projectVersion=3.9.2 projectGroup=io.micronaut.aws micronautDocsVersion=2.0.0 From 78fa9c70a4f90485e04efe55ad66c8a971052222 Mon Sep 17 00:00:00 2001 From: micronaut-build Date: Fri, 16 Sep 2022 15:50:48 +0000 Subject: [PATCH 29/51] Back to 3.9.3-SNAPSHOT --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index c8bbb6d387..2f6600fc6b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -projectVersion=3.9.2 +projectVersion=3.9.3-SNAPSHOT projectGroup=io.micronaut.aws micronautDocsVersion=2.0.0 From de12fb1c955bc30fcf72aee151e19dc8659d6564 Mon Sep 17 00:00:00 2001 From: Miguel Ferreira Date: Wed, 28 Sep 2022 13:49:11 +0200 Subject: [PATCH 30/51] Fix KMS typo in javadoc of parameter store config (#1475) --- .../aws/parameterstore/AWSParameterStoreConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws-parameter-store/src/main/java/io/micronaut/discovery/aws/parameterstore/AWSParameterStoreConfiguration.java b/aws-parameter-store/src/main/java/io/micronaut/discovery/aws/parameterstore/AWSParameterStoreConfiguration.java index bab572951d..ab7d431c4f 100644 --- a/aws-parameter-store/src/main/java/io/micronaut/discovery/aws/parameterstore/AWSParameterStoreConfiguration.java +++ b/aws-parameter-store/src/main/java/io/micronaut/discovery/aws/parameterstore/AWSParameterStoreConfiguration.java @@ -98,7 +98,7 @@ public boolean getUseSecureParameters() { } /** - * Use auto-decryption via MKS for SecureString parameters. Default value ({@value #DEFAULT_SECURE}). + * Use auto-decryption via KMS for SecureString parameters. Default value ({@value #DEFAULT_SECURE}). * If set to false, you will not get unencrypted values. * * @param useSecureParameters True if secure parameters should be used From eafccaa0c6e40ff505bafcb238ddda13c7c00a0f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 7 Oct 2022 11:59:50 +0200 Subject: [PATCH 31/51] Update dependency org.jetbrains.kotlin:kotlin-stdlib-jdk8 to v1.7.20 (#1476) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- test-suite-kotlin/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-suite-kotlin/build.gradle.kts b/test-suite-kotlin/build.gradle.kts index ca51502189..a5df3ea661 100644 --- a/test-suite-kotlin/build.gradle.kts +++ b/test-suite-kotlin/build.gradle.kts @@ -18,7 +18,7 @@ dependencies { testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") testImplementation(project(":function-aws")) - testImplementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.10") + testImplementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.20") testImplementation(project(":function-client-aws")) } From 9af485b8b05e20bea2d45a8369a8e01256e51d29 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 7 Oct 2022 12:00:27 +0200 Subject: [PATCH 32/51] Update mikepenz/action-junit-report action to v3.5.1 (#1469) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/graalvm.yml | 2 +- .github/workflows/gradle.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/graalvm.yml b/.github/workflows/graalvm.yml index b0b11df674..8f3f1c04aa 100644 --- a/.github/workflows/graalvm.yml +++ b/.github/workflows/graalvm.yml @@ -58,7 +58,7 @@ jobs: PREDICTIVE_TEST_SELECTION: "${{ github.event_name == 'pull_request' && 'true' || 'false' }}" - name: Publish Test Report if: always() - uses: mikepenz/action-junit-report@v3.4.0 + uses: mikepenz/action-junit-report@v3.5.1 with: check_name: GraalVM CE CI / Test Report (Java ${{ matrix.java }}) report_paths: '**/build/test-results/test/TEST-*.xml' diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 44d493a343..51d9160636 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -60,7 +60,7 @@ jobs: PREDICTIVE_TEST_SELECTION: "${{ github.event_name == 'pull_request' && 'true' || 'false' }}" - name: Publish Test Report if: always() - uses: mikepenz/action-junit-report@v3.4.0 + uses: mikepenz/action-junit-report@v3.5.1 with: check_name: Java CI / Test Report (${{ matrix.java }}) report_paths: '**/build/test-results/test/TEST-*.xml' From 6d1d90e8233bd5d04d648e04c4d8638a11d022f4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 7 Oct 2022 12:00:39 +0200 Subject: [PATCH 33/51] Update aws-java-sdk monorepo to v1.12.318 (#1466) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ff62aba253..a17dde2913 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,7 @@ fileupload = '0.0.5' jetty = '9.4.48.v20220622' logback-json-classic = '0.1.5' managed-alexa-ask-sdk = "2.44.0" -managed-aws-java-sdk-v1 = '1.12.301' +managed-aws-java-sdk-v1 = '1.12.318' managed-aws-java-sdk-v2 = '2.17.271' managed-aws-lambda = '1.2.1' managed-aws-lambda-events = '3.11.0' From 5c3844197d73ba8031f2eb1152d6e1c52d99e948 Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Fri, 7 Oct 2022 12:03:23 +0200 Subject: [PATCH 34/51] ci: projectVersion 3.10.0-SNAPSHOT [ci skip] --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 2f6600fc6b..1564fd908f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -projectVersion=3.9.3-SNAPSHOT +projectVersion=3.10.0-SNAPSHOT projectGroup=io.micronaut.aws micronautDocsVersion=2.0.0 From 6eaa67aec4997a72005a7371a3bf889937a62024 Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Fri, 7 Oct 2022 12:03:33 +0200 Subject: [PATCH 35/51] ci: githubCoreBranch to 3.8.x [ci skip] --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 1564fd908f..d2d8632ebb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,7 +14,7 @@ projectUrl=https://micronaut.io githubSlug=micronaut-projects/micronaut-aws developers=Graeme Rocher -githubCoreBranch=3.7.x +githubCoreBranch=3.8.x bomProperty=micronautAwsVersion org.gradle.caching=true org.gradle.parallel=true From 3e886cac23d6ac8fa70058187e213083707e8087 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 7 Oct 2022 12:04:58 +0200 Subject: [PATCH 36/51] build: Update micronautVersion to v3.7.1 (#1474) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index d2d8632ebb..73305aea61 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ projectVersion=3.10.0-SNAPSHOT projectGroup=io.micronaut.aws micronautDocsVersion=2.0.0 -micronautVersion=3.6.3 +micronautVersion=3.7.1 micronautTestVersion=3.5.0 groovyVersion=3.0.10 From 2e0fbd1e2230cf4db954b322598a9c613145ee8e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 7 Oct 2022 12:05:09 +0200 Subject: [PATCH 37/51] Update dependency software.amazon.awscdk:aws-cdk-lib to v2.45.0 (#1471) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a17dde2913..ea3c406529 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,7 +9,7 @@ managed-aws-java-sdk-v2 = '2.17.271' managed-aws-lambda = '1.2.1' managed-aws-lambda-events = '3.11.0' managed-aws-serverless-core = '1.8.2' -managed-aws-cdk-lib='2.41.0' +managed-aws-cdk-lib='2.45.0' servlet-api = "2.5" slf4j = "1.7.36" From 3e96bd53b9869fb1141c9304974ff3e34b3beee7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 7 Oct 2022 12:05:32 +0200 Subject: [PATCH 38/51] fix(deps): update dependency org.eclipse.jetty:jetty-server to v9.4.49.v20220914 (#1468) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ea3c406529..d83975a357 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] bouncycastle = '1.70' fileupload = '0.0.5' -jetty = '9.4.48.v20220622' +jetty = '9.4.49.v20220914' logback-json-classic = '0.1.5' managed-alexa-ask-sdk = "2.44.0" managed-aws-java-sdk-v1 = '1.12.318' From 679f82039dc5043d05b9b6ff97bceb3d12ac5063 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 7 Oct 2022 13:45:49 +0200 Subject: [PATCH 39/51] Update aws-java-sdk-v2 monorepo to v2.17.289 (#1467) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Sergio del Amo --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d83975a357..de185fa0ea 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ jetty = '9.4.49.v20220914' logback-json-classic = '0.1.5' managed-alexa-ask-sdk = "2.44.0" managed-aws-java-sdk-v1 = '1.12.318' -managed-aws-java-sdk-v2 = '2.17.271' +managed-aws-java-sdk-v2 = '2.17.289' managed-aws-lambda = '1.2.1' managed-aws-lambda-events = '3.11.0' managed-aws-serverless-core = '1.8.2' From 2a279e2defe3e849b0d0e78bb06f6765a9656ec0 Mon Sep 17 00:00:00 2001 From: Slavko Date: Wed, 12 Oct 2022 17:41:26 +0200 Subject: [PATCH 40/51] Issue 1108 - Add support for Secret Manager for RDS instance (#1481) Issue 1108 - Add support for Secret Manager for RDS instance --- .../AwsDistributedConfigurationClient.java | 66 ++++++++++------- .../AwsDistributedConfigurationSpec.groovy | 10 ++- .../SecretsKeyValueFetcher.java | 30 ++++++++ .../SecretsManagerConfiguration.java | 11 +++ .../SecretsManagerConfigurationClient.java | 44 ++++++++++- ...SecretsManagerConfigurationProperties.java | 58 +++++++++++++++ ...sManagerGroupNameAwareKeyValueFetcher.java | 74 +++++++++++++++++++ .../SecretsManagerKeyValueFetcher.java | 49 ++++++++---- ...cretsManagerConfigurationClientSpec.groovy | 20 +++++ .../SecretsManagerKeyValueFetcherSpec.groovy | 35 ++++++--- ...retsManagerSecretsConfigurationSpec.groovy | 35 +++++++++ 11 files changed, 373 insertions(+), 59 deletions(-) create mode 100644 aws-secretsmanager/src/main/java/io/micronaut/aws/secretsmanager/SecretsKeyValueFetcher.java create mode 100644 aws-secretsmanager/src/main/java/io/micronaut/aws/secretsmanager/SecretsManagerGroupNameAwareKeyValueFetcher.java create mode 100644 aws-secretsmanager/src/test/groovy/io/micronaut/aws/secretsmanager/SecretsManagerSecretsConfigurationSpec.groovy diff --git a/aws-distributed-configuration/src/main/java/io/micronaut/aws/distributedconfiguration/AwsDistributedConfigurationClient.java b/aws-distributed-configuration/src/main/java/io/micronaut/aws/distributedconfiguration/AwsDistributedConfigurationClient.java index 9c8620a296..4449e71d2b 100644 --- a/aws-distributed-configuration/src/main/java/io/micronaut/aws/distributedconfiguration/AwsDistributedConfigurationClient.java +++ b/aws-distributed-configuration/src/main/java/io/micronaut/aws/distributedconfiguration/AwsDistributedConfigurationClient.java @@ -26,13 +26,13 @@ import org.reactivestreams.Publisher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Set; /** * Base implementation for AWS services contributing distributed configuration. @@ -52,7 +52,7 @@ public abstract class AwsDistributedConfigurationClient implements Configuration /** * * @param awsDistributedConfiguration AWS Distributed Configuration - * @param keyValueFetcher a Key Value Fetcher + * @param keyValueFetcher Key Value Fetcher * @param applicationConfiguration Application Configuration */ public AwsDistributedConfigurationClient(AwsDistributedConfiguration awsDistributedConfiguration, @@ -73,38 +73,41 @@ public AwsDistributedConfigurationClient(AwsDistributedConfiguration awsDistribu @Override public Publisher getPropertySources(Environment environment) { List configurationResolutionPrefixes = generateConfigurationResolutionPrefixes(environment); - - Map configurationResolutionPrefixesValues = new HashMap<>(); + Map>> configurationResolutionPrefixKeyValueGroups = new LinkedHashMap<>(); + int allKeysCount = 0; for (String prefix : configurationResolutionPrefixes) { - Optional keyValuesOptional = keyValueFetcher.keyValuesByPrefix(prefix); - if (keyValuesOptional.isPresent()) { - Map keyValues = keyValuesOptional.get(); - configurationResolutionPrefixesValues.put(prefix, keyValues); + Optional keyValueGroupsOptional = keyValueFetcher.keyValuesByPrefix(prefix); + if (keyValueGroupsOptional.isPresent()) { + Map> keyValueGroups = keyValueGroupsOptional.get(); + configurationResolutionPrefixKeyValueGroups.put(prefix, keyValueGroups); + for (Map keyValues: keyValueGroups.values()) { + allKeysCount += keyValues.size(); + } } } - Set allKeys = new HashSet<>(); - for (Map m : configurationResolutionPrefixesValues.values()) { - allKeys.addAll(m.keySet()); - } - Map result = new HashMap<>(); if (LOG.isTraceEnabled()) { - LOG.trace("evaluating {} keys", allKeys.size()); + LOG.trace("evaluating {} keys", allKeysCount); } - for (String k : allKeys) { - if (!result.containsKey(k)) { - for (String prefix : configurationResolutionPrefixes) { - if (configurationResolutionPrefixesValues.containsKey(prefix)) { - Map values = configurationResolutionPrefixesValues.get(prefix); - if (values.containsKey(k)) { - if (LOG.isTraceEnabled()) { - LOG.trace("adding property {} from prefix {}", k, prefix); - } - result.put(k, values.get(k)); - break; + Map result = new HashMap<>(); + for (Map.Entry>> configurationMapEntry : configurationResolutionPrefixKeyValueGroups.entrySet()) { + String prefix = configurationMapEntry.getKey(); + Map> keyValueGroups = configurationMapEntry.getValue(); + for (Map.Entry> keyValueGroupEntry : keyValueGroups.entrySet()) { + String groupName = keyValueGroupEntry.getKey(); + Map keyValues = keyValueGroupEntry.getValue(); + + for (Map.Entry keyValuesEntry: keyValues.entrySet()) { + String key = keyValuesEntry.getKey(); + String adaptedPropertyKey = adaptPropertyKey(key, groupName); + if (!result.containsKey(adaptedPropertyKey)) { + if (LOG.isTraceEnabled()) { + LOG.trace("adding property {} from prefix {}", adaptedPropertyKey, prefix); } + result.put(adaptedPropertyKey, keyValuesEntry.getValue()); } } + } } String propertySourceName = getPropertySourceName(); @@ -119,6 +122,17 @@ public Publisher getPropertySources(Environment environment) { return Publishers.just(new MapPropertySource(propertySourceName, result)); } + /** + * Adapts an original key. For example, key could be appended to a prefix in order to avoid naming ambiguity. + * * + * @since 3.8.0 + * @param originalKey an original property key + * @param groupName a property group name + * @return An adapted property key (e.g. key that has been appended to a prefix) + */ + @NonNull + protected abstract String adaptPropertyKey(String originalKey, String groupName); + /** * * @return The name of the property source diff --git a/aws-distributed-configuration/src/test/groovy/io/micronaut/aws/distributedconfiguration/AwsDistributedConfigurationSpec.groovy b/aws-distributed-configuration/src/test/groovy/io/micronaut/aws/distributedconfiguration/AwsDistributedConfigurationSpec.groovy index e26caa603c..a5268c4656 100644 --- a/aws-distributed-configuration/src/test/groovy/io/micronaut/aws/distributedconfiguration/AwsDistributedConfigurationSpec.groovy +++ b/aws-distributed-configuration/src/test/groovy/io/micronaut/aws/distributedconfiguration/AwsDistributedConfigurationSpec.groovy @@ -99,6 +99,12 @@ class AwsDistributedConfigurationSpec extends Specification { super(awsDistributedConfiguration, keyValueFetcher, applicationConfiguration) } + @Override + @NonNull + protected String adaptPropertyKey(String originalKey, String groupName) { + return originalKey + } + @Override @NonNull protected String getPropertySourceName() { @@ -137,9 +143,9 @@ class AwsDistributedConfigurationSpec extends Specification { ] @Override - Optional keyValuesByPrefix(@NonNull String prefix) { + Optional> keyValuesByPrefix(@NonNull String prefix) { String k = m.keySet().find { it.startsWith(prefix) } - (k) ? Optional.of(m[k]) : Optional.empty() + (k) ? Optional.of([(k): m[k]] as Map) : Optional.empty() } } } diff --git a/aws-secretsmanager/src/main/java/io/micronaut/aws/secretsmanager/SecretsKeyValueFetcher.java b/aws-secretsmanager/src/main/java/io/micronaut/aws/secretsmanager/SecretsKeyValueFetcher.java new file mode 100644 index 0000000000..bb544fb74e --- /dev/null +++ b/aws-secretsmanager/src/main/java/io/micronaut/aws/secretsmanager/SecretsKeyValueFetcher.java @@ -0,0 +1,30 @@ +/* + * Copyright 2017-2021 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.aws.secretsmanager; + +import io.micronaut.aws.distributedconfiguration.KeyValueFetcher; +import io.micronaut.context.annotation.DefaultImplementation; + +/** + * Key Value fetcher for AWS Secrets Manager. + * {@link KeyValueFetcher} + * + * @author sbodvanski + * @since 3.8.0 + */ +@DefaultImplementation(SecretsManagerGroupNameAwareKeyValueFetcher.class) +public interface SecretsKeyValueFetcher extends KeyValueFetcher { +} diff --git a/aws-secretsmanager/src/main/java/io/micronaut/aws/secretsmanager/SecretsManagerConfiguration.java b/aws-secretsmanager/src/main/java/io/micronaut/aws/secretsmanager/SecretsManagerConfiguration.java index 82ce8be887..3dcc901f17 100644 --- a/aws-secretsmanager/src/main/java/io/micronaut/aws/secretsmanager/SecretsManagerConfiguration.java +++ b/aws-secretsmanager/src/main/java/io/micronaut/aws/secretsmanager/SecretsManagerConfiguration.java @@ -17,10 +17,21 @@ import io.micronaut.core.util.Toggleable; +import java.util.List; + /** * Configuration for Secrets Manager. * @author Sergio del Amo * @since 2.8.0 */ public interface SecretsManagerConfiguration extends Toggleable { + + /** + * Provide a list of secret configurations that allows for flexibility in secret key naming. + * This is provided by an option to define a key group prefix for any secret name. + * + * @since 3.8.0 + * @return the AWS Secrets Manager secrets configuration. + */ + List getSecrets(); } diff --git a/aws-secretsmanager/src/main/java/io/micronaut/aws/secretsmanager/SecretsManagerConfigurationClient.java b/aws-secretsmanager/src/main/java/io/micronaut/aws/secretsmanager/SecretsManagerConfigurationClient.java index 0d12fd486c..a6ac79ce5a 100644 --- a/aws-secretsmanager/src/main/java/io/micronaut/aws/secretsmanager/SecretsManagerConfigurationClient.java +++ b/aws-secretsmanager/src/main/java/io/micronaut/aws/secretsmanager/SecretsManagerConfigurationClient.java @@ -19,12 +19,15 @@ import io.micronaut.aws.distributedconfiguration.AwsDistributedConfigurationClient; import io.micronaut.context.annotation.BootstrapContextCompatible; import io.micronaut.context.annotation.Requires; +import io.micronaut.core.annotation.Creator; import io.micronaut.core.annotation.NonNull; import io.micronaut.core.annotation.Nullable; +import io.micronaut.core.util.CollectionUtils; import io.micronaut.runtime.ApplicationConfiguration; - import jakarta.inject.Singleton; +import java.util.Optional; + /** * Distributed configuration client for AWS Secrets Manager. * @see AWS Secrets Manager @@ -33,22 +36,55 @@ */ @Requires(beans = { AwsDistributedConfiguration.class, - SecretsManagerKeyValueFetcher.class + SecretsManagerGroupNameAwareKeyValueFetcher.class }) @Singleton @BootstrapContextCompatible public class SecretsManagerConfigurationClient extends AwsDistributedConfigurationClient { + private final Optional secretsManagerConfiguration; + /** - * * @param awsDistributedConfiguration AWS Distributed Configuration * @param secretsManagerKeyValueFetcher Secrets Manager Key Value Fetcher * @param applicationConfiguration Application Configuration */ + @Deprecated public SecretsManagerConfigurationClient(AwsDistributedConfiguration awsDistributedConfiguration, SecretsManagerKeyValueFetcher secretsManagerKeyValueFetcher, @Nullable ApplicationConfiguration applicationConfiguration) { + this(awsDistributedConfiguration, secretsManagerKeyValueFetcher, applicationConfiguration, null); + } + + /** + * @param awsDistributedConfiguration AWS Distributed Configuration + * @param secretsManagerKeyValueFetcher Secrets Manager Key Value Fetcher + * @param applicationConfiguration Application Configuration + * @param secretsManagerConfiguration Secrets Configuration + */ + @Creator + public SecretsManagerConfigurationClient(AwsDistributedConfiguration awsDistributedConfiguration, + SecretsManagerKeyValueFetcher secretsManagerKeyValueFetcher, + @Nullable ApplicationConfiguration applicationConfiguration, + SecretsManagerConfiguration secretsManagerConfiguration) { super(awsDistributedConfiguration, secretsManagerKeyValueFetcher, applicationConfiguration); + this.secretsManagerConfiguration = Optional.of(secretsManagerConfiguration); + } + + @Override + @NonNull + protected String adaptPropertyKey(String originalKey, String groupName) { + if (secretsManagerConfiguration.isPresent()) { + SecretsManagerConfiguration secretsConfiguration = secretsManagerConfiguration.get(); + if (CollectionUtils.isNotEmpty(secretsConfiguration.getSecrets())) { + for (SecretsManagerConfigurationProperties.SecretConfiguration secret : secretsConfiguration.getSecrets()) { + if (groupName.endsWith(secret.getSecretName())) { + return secret.getPrefix() + "." + originalKey; + } + } + } + } + return originalKey; } @Override @@ -58,7 +94,9 @@ protected String getPropertySourceName() { } @Override + @NonNull public String getDescription() { return "AWS Secrets Manager"; } + } diff --git a/aws-secretsmanager/src/main/java/io/micronaut/aws/secretsmanager/SecretsManagerConfigurationProperties.java b/aws-secretsmanager/src/main/java/io/micronaut/aws/secretsmanager/SecretsManagerConfigurationProperties.java index fa8eb66c79..f725cc2f55 100644 --- a/aws-secretsmanager/src/main/java/io/micronaut/aws/secretsmanager/SecretsManagerConfigurationProperties.java +++ b/aws-secretsmanager/src/main/java/io/micronaut/aws/secretsmanager/SecretsManagerConfigurationProperties.java @@ -18,6 +18,10 @@ import io.micronaut.aws.AWSConfiguration; import io.micronaut.context.annotation.BootstrapContextCompatible; import io.micronaut.context.annotation.ConfigurationProperties; +import io.micronaut.context.annotation.EachProperty; +import io.micronaut.core.annotation.Introspected; + +import java.util.List; /** * {@link ConfigurationProperties} implementation of {@link SecretsManagerConfiguration}. @@ -41,6 +45,8 @@ public class SecretsManagerConfigurationProperties implements SecretsManagerConf private boolean enabled = DEFAULT_ENABLED; + protected List secrets; + /** * @return Whether the AWS Secrets Manager configuration is enabled */ @@ -56,4 +62,56 @@ public boolean isEnabled() { public void setEnabled(boolean enabled) { this.enabled = enabled; } + + @Override + public List getSecrets() { + return secrets; + } + + /** + * Secret configuration holder that allows for flexibility in secret key naming in the Micronaut context to avoid a potential keys name collision. + * This is provided by an option to define a key group prefix for any secret name. + * + * @author sbodvanski + * @since 3.8.0 + */ + @Introspected + @EachProperty(value = "secrets", list = true) + @BootstrapContextCompatible + public static class SecretConfiguration { + private String secretName; + private String prefix; + + /** + * Sets secret name. + * + * @param secretName secret name + */ + public void setSecretName(String secretName) { + this.secretName = secretName; + } + + /** + * Sets the group key prefix. + * + * @param prefix prefix + */ + public void setPrefix(String prefix) { + this.prefix = prefix; + } + + /** + * @return a secret name + */ + public String getSecretName() { + return secretName; + } + + /** + * @return a secret key group prefix + */ + public String getPrefix() { + return prefix; + } + } } diff --git a/aws-secretsmanager/src/main/java/io/micronaut/aws/secretsmanager/SecretsManagerGroupNameAwareKeyValueFetcher.java b/aws-secretsmanager/src/main/java/io/micronaut/aws/secretsmanager/SecretsManagerGroupNameAwareKeyValueFetcher.java new file mode 100644 index 0000000000..abc5583d87 --- /dev/null +++ b/aws-secretsmanager/src/main/java/io/micronaut/aws/secretsmanager/SecretsManagerGroupNameAwareKeyValueFetcher.java @@ -0,0 +1,74 @@ +/* + * Copyright 2017-2021 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.aws.secretsmanager; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.micronaut.context.annotation.BootstrapContextCompatible; +import io.micronaut.context.annotation.Replaces; +import io.micronaut.context.annotation.Requires; +import io.micronaut.core.annotation.Experimental; +import io.micronaut.core.annotation.NonNull; +import jakarta.inject.Singleton; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; +import software.amazon.awssdk.services.secretsmanager.model.SecretListEntry; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +/** + * Key Value fetcher for AWS Secrets Manager that is aware of Secret Key Value group names. + * + * @author sbodvanski + * @since 3.8.0 + */ +@Experimental +@Requires(beans = {SecretsManagerClient.class}) +@BootstrapContextCompatible +@Singleton +@Replaces(SecretsManagerKeyValueFetcher.class) +public class SecretsManagerGroupNameAwareKeyValueFetcher extends SecretsManagerKeyValueFetcher { + private static final Logger LOG = LoggerFactory.getLogger(SecretsManagerGroupNameAwareKeyValueFetcher.class); + + /** + * @param secretsClient Secrets Client + * @param objectMapper Object Mapper + */ + public SecretsManagerGroupNameAwareKeyValueFetcher(SecretsManagerClient secretsClient, + ObjectMapper objectMapper) { + super(secretsClient, objectMapper); + } + + @Override + @NonNull + protected void addSecretDetailsToResults(SecretListEntry secret, Map result) { + Map keyValues = new HashMap<>(); + Optional secretValueOptional = fetchSecretValue(secretsClient, secret.name()); + if (secretValueOptional.isPresent()) { + try { + keyValues.putAll(objectMapper.readValue(secretValueOptional.get(), Map.class)); + result.put(secret.name(), keyValues); + } catch (JsonProcessingException e) { + if (LOG.isWarnEnabled()) { + LOG.warn("could not read secret ({}) value from JSON to Map", secret.name()); + } + } + } + } +} diff --git a/aws-secretsmanager/src/main/java/io/micronaut/aws/secretsmanager/SecretsManagerKeyValueFetcher.java b/aws-secretsmanager/src/main/java/io/micronaut/aws/secretsmanager/SecretsManagerKeyValueFetcher.java index 5b739f2169..8caef9fea6 100644 --- a/aws-secretsmanager/src/main/java/io/micronaut/aws/secretsmanager/SecretsManagerKeyValueFetcher.java +++ b/aws-secretsmanager/src/main/java/io/micronaut/aws/secretsmanager/SecretsManagerKeyValueFetcher.java @@ -17,7 +17,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import io.micronaut.aws.distributedconfiguration.KeyValueFetcher; import io.micronaut.context.annotation.BootstrapContextCompatible; import io.micronaut.context.annotation.Requires; import io.micronaut.core.annotation.Experimental; @@ -46,7 +45,7 @@ import java.util.Optional; /** - * {@link KeyValueFetcher} implementations for AWS Secrets Manager. + * {@link SecretsKeyValueFetcher} implementations for AWS Secrets Manager. * @author Sergio del Amo * @since 2.8.0 */ @@ -54,11 +53,11 @@ @Requires(beans = {SecretsManagerClient.class}) @BootstrapContextCompatible @Singleton -public class SecretsManagerKeyValueFetcher implements KeyValueFetcher { +public class SecretsManagerKeyValueFetcher implements SecretsKeyValueFetcher { private static final Logger LOG = LoggerFactory.getLogger(SecretsManagerKeyValueFetcher.class); - private final SecretsManagerClient secretsClient; - private final ObjectMapper objectMapper; + protected final SecretsManagerClient secretsClient; + protected final ObjectMapper objectMapper; /** * @@ -101,16 +100,7 @@ public Optional keyValuesByPrefix(@NonNull String prefix) { if (LOG.isTraceEnabled()) { LOG.trace("Evaluating secret {}", secret.name()); } - Optional secretValueOptional = fetchSecretValue(secretsClient, secret.name()); - if (secretValueOptional.isPresent()) { - try { - result.putAll(objectMapper.readValue(secretValueOptional.get(), Map.class)); - } catch (JsonProcessingException e) { - if (LOG.isWarnEnabled()) { - LOG.warn("could not read secret ({}) value from JSON to Map", secret.name()); - } - } - } + addSecretDetailsToResults(secret, result); } nextToken = secretsResponse.nextToken(); @@ -126,8 +116,35 @@ public Optional keyValuesByPrefix(@NonNull String prefix) { return result.isEmpty() ? Optional.empty() : Optional.of(result); } + /** + * Add secret details to the result map. + * + * @param secret a secret list entry + * @param result a map that collects the results + */ + @NonNull + protected void addSecretDetailsToResults(SecretListEntry secret, Map result) { + Optional secretValueOptional = fetchSecretValue(secretsClient, secret.name()); + if (secretValueOptional.isPresent()) { + try { + result.putAll(objectMapper.readValue(secretValueOptional.get(), Map.class)); + } catch (JsonProcessingException e) { + if (LOG.isWarnEnabled()) { + LOG.warn("could not read secret ({}) value from JSON to Map", secret.name()); + } + } + } + } + + /** + * Fetches secret value. + * + * @param secretsClient a secret manager cleint + * @param secretName a secret name + * @return secret value optional + */ @NonNull - private Optional fetchSecretValue(@NonNull SecretsManagerClient secretsClient, + protected Optional fetchSecretValue(@NonNull SecretsManagerClient secretsClient, @NonNull String secretName) { GetSecretValueRequest getSecretValueRequest = GetSecretValueRequest.builder() .secretId(secretName) diff --git a/aws-secretsmanager/src/test/groovy/io/micronaut/aws/secretsmanager/SecretsManagerConfigurationClientSpec.groovy b/aws-secretsmanager/src/test/groovy/io/micronaut/aws/secretsmanager/SecretsManagerConfigurationClientSpec.groovy index a88589fb26..69da61d38a 100644 --- a/aws-secretsmanager/src/test/groovy/io/micronaut/aws/secretsmanager/SecretsManagerConfigurationClientSpec.groovy +++ b/aws-secretsmanager/src/test/groovy/io/micronaut/aws/secretsmanager/SecretsManagerConfigurationClientSpec.groovy @@ -14,6 +14,17 @@ class SecretsManagerConfigurationClientSpec extends ApplicationContextSpecificat 'SecretsManagerConfigurationClientSpec' } + @Override + Map getConfiguration() { + super.configuration + [ + 'aws.secretsmanager.enabled': true, + 'aws.secretsmanager.secrets': [ + ["secret-name": "rds_default", "prefix": "datasources.default"], + ["secret-name": "rds_backup", "prefix": "datasources.backup"] + ] + ] + } + void "SecretsManagerConfigurationClient is annotated with BootstrapContextCompatible"() { when: BeanDefinition beanDefinition = applicationContext.getBeanDefinition(SecretsManagerConfigurationClient) @@ -22,6 +33,15 @@ class SecretsManagerConfigurationClientSpec extends ApplicationContextSpecificat beanDefinition.getAnnotationNameByStereotype(BootstrapContextCompatible).isPresent() } + void "The adaptPropertyKey method call when secret manager configuration is provided"() { + when: + SecretsManagerConfigurationClient bean = applicationContext.getBean(SecretsManagerConfigurationClient) + String adaptedPropertyKey = bean.adaptPropertyKey('host', 'rds_default') + + then: + adaptedPropertyKey == 'datasources.default.host' + } + @Requires(property = 'spec.name', value = 'SecretsManagerConfigurationClientSpec') @Primary @Singleton diff --git a/aws-secretsmanager/src/test/groovy/io/micronaut/aws/secretsmanager/SecretsManagerKeyValueFetcherSpec.groovy b/aws-secretsmanager/src/test/groovy/io/micronaut/aws/secretsmanager/SecretsManagerKeyValueFetcherSpec.groovy index 586f14ffb0..10699d5a7a 100644 --- a/aws-secretsmanager/src/test/groovy/io/micronaut/aws/secretsmanager/SecretsManagerKeyValueFetcherSpec.groovy +++ b/aws-secretsmanager/src/test/groovy/io/micronaut/aws/secretsmanager/SecretsManagerKeyValueFetcherSpec.groovy @@ -30,7 +30,7 @@ class SecretsManagerKeyValueFetcherSpec extends ApplicationContextSpecification void "SecretsManagerKeyValueFetcher is annotated with BootstrapContextCompatible"() { when: - BeanDefinition beanDefinition = applicationContext.getBeanDefinition(SecretsManagerKeyValueFetcher) + BeanDefinition beanDefinition = applicationContext.getBeanDefinition(SecretsManagerGroupNameAwareKeyValueFetcher) then: beanDefinition.getAnnotationNameByStereotype(BootstrapContextCompatible).isPresent() @@ -38,7 +38,7 @@ class SecretsManagerKeyValueFetcherSpec extends ApplicationContextSpecification void "bean of type SecretsManagerKeyValueFetcher exists"() { when: - SecretsManagerKeyValueFetcher secretsManagerKeyValueFetcher = applicationContext.getBean(SecretsManagerKeyValueFetcher) + SecretsManagerGroupNameAwareKeyValueFetcher secretsManagerKeyValueFetcher = applicationContext.getBean(SecretsManagerGroupNameAwareKeyValueFetcher) then: noExceptionThrown() @@ -50,18 +50,29 @@ class SecretsManagerKeyValueFetcherSpec extends ApplicationContextSpecification mapOptional.isPresent() when: - Map map = mapOptional.get() + Map keyValueGroups = mapOptional.get() then: - map - map.containsKey('micronaut.security.oauth2.clients.companyauthserver.client-id') - map['micronaut.security.oauth2.clients.companyauthserver.client-id'] == 'XXX' - map.containsKey('micronaut.security.oauth2.clients.companyauthserver.client-secret') - map['micronaut.security.oauth2.clients.companyauthserver.client-secret'] == 'YYY' - map.containsKey('micronaut.security.oauth2.clients.google.client-id') - map['micronaut.security.oauth2.clients.google.client-id'] == 'ZZZ' - map.containsKey('micronaut.security.oauth2.clients.google.client-secret') - map['micronaut.security.oauth2.clients.google.client-secret'] == 'PPP' + keyValueGroups.containsKey('/config/myapp_dev/oauthcompanyauthserver') + keyValueGroups.containsKey('/config/myapp_dev/oauthgoogle') + + when: + Map keyValues = keyValueGroups['/config/myapp_dev/oauthcompanyauthserver'] + + then: + keyValues.containsKey('micronaut.security.oauth2.clients.companyauthserver.client-id') + keyValues['micronaut.security.oauth2.clients.companyauthserver.client-id'] == 'XXX' + keyValues.containsKey('micronaut.security.oauth2.clients.companyauthserver.client-secret') + keyValues['micronaut.security.oauth2.clients.companyauthserver.client-secret'] == 'YYY' + + when: + keyValues = keyValueGroups['/config/myapp_dev/oauthgoogle'] + + then: + keyValues.containsKey('micronaut.security.oauth2.clients.google.client-id') + keyValues['micronaut.security.oauth2.clients.google.client-id'] == 'ZZZ' + keyValues.containsKey('micronaut.security.oauth2.clients.google.client-secret') + keyValues['micronaut.security.oauth2.clients.google.client-secret'] == 'PPP' } @Requires(property = 'spec.name', value = 'SecretsManagerKeyValueFetcherSpec') diff --git a/aws-secretsmanager/src/test/groovy/io/micronaut/aws/secretsmanager/SecretsManagerSecretsConfigurationSpec.groovy b/aws-secretsmanager/src/test/groovy/io/micronaut/aws/secretsmanager/SecretsManagerSecretsConfigurationSpec.groovy new file mode 100644 index 0000000000..bc77cd532c --- /dev/null +++ b/aws-secretsmanager/src/test/groovy/io/micronaut/aws/secretsmanager/SecretsManagerSecretsConfigurationSpec.groovy @@ -0,0 +1,35 @@ +package io.micronaut.aws.secretsmanager + +class SecretsManagerSecretsConfigurationSpec extends ApplicationContextSpecification { + + @Override + Map getConfiguration() { + super.configuration + [ + 'aws.secretsmanager.enabled': true, + 'aws.secretsmanager.secrets': [ + ["secret-name": "rds_default", "prefix": "datasources.default"], + ["secret-name": "rds_backup", "prefix": "datasources.backup"] + ] + ] + } + + void "secrets manager loads secrets configuration"() { + def secretsManagerConfiguration = applicationContext.getBean(SecretsManagerConfiguration) + + expect: + !secretsManagerConfiguration.getSecrets().isEmpty() + + when: + List secretConfigurations = secretsManagerConfiguration.getSecrets() + + then: + secretConfigurations.size() == 2 + for (secretConfiguration in secretConfigurations) { + if (secretConfiguration.getSecretName() == 'rds_default') { + secretConfiguration.getPrefix() == 'datasources.default' + } else if (secretConfiguration.getSecretName() == 'rds_backup') { + secretConfiguration.getPrefix() == 'datasources.backup' + } + } + } +} From c8ad16556577e53eaea2a7d6f7f7496ff404b6d7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 13 Oct 2022 13:19:22 +0200 Subject: [PATCH 41/51] build: Update aws-java-sdk-v2 monorepo to v2.17.290 (#1480) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index de185fa0ea..ba517c453e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ jetty = '9.4.49.v20220914' logback-json-classic = '0.1.5' managed-alexa-ask-sdk = "2.44.0" managed-aws-java-sdk-v1 = '1.12.318' -managed-aws-java-sdk-v2 = '2.17.289' +managed-aws-java-sdk-v2 = '2.17.290' managed-aws-lambda = '1.2.1' managed-aws-lambda-events = '3.11.0' managed-aws-serverless-core = '1.8.2' From ee241434c0a8d0c141c336386120a27d5de408c2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 13 Oct 2022 13:20:11 +0200 Subject: [PATCH 42/51] Update dependency io.micronaut.test:micronaut-test-spock to v3.7.0 (#1470) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 73305aea61..ce3c0a57eb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ projectGroup=io.micronaut.aws micronautDocsVersion=2.0.0 micronautVersion=3.7.1 -micronautTestVersion=3.5.0 +micronautTestVersion=3.7.0 groovyVersion=3.0.10 spockVersion=2.1-groovy-3.0 From 66ce78f304aebc1523d5ae7467824e3a5b1aa369 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 13 Oct 2022 13:22:09 +0200 Subject: [PATCH 43/51] Update dependency com.amazonaws.serverless:aws-serverless-java-container-core to v1.9 (#1473) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ba517c453e..cd4163f10e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ managed-aws-java-sdk-v1 = '1.12.318' managed-aws-java-sdk-v2 = '2.17.290' managed-aws-lambda = '1.2.1' managed-aws-lambda-events = '3.11.0' -managed-aws-serverless-core = '1.8.2' +managed-aws-serverless-core = '1.9' managed-aws-cdk-lib='2.45.0' servlet-api = "2.5" slf4j = "1.7.36" From d5cfb074c9653e48382cff89677c790d9f027297 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 14 Oct 2022 12:14:59 +0200 Subject: [PATCH 44/51] Update aws-java-sdk monorepo to v1.12.320 (#1479) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index cd4163f10e..883607bc21 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,7 @@ fileupload = '0.0.5' jetty = '9.4.49.v20220914' logback-json-classic = '0.1.5' managed-alexa-ask-sdk = "2.44.0" -managed-aws-java-sdk-v1 = '1.12.318' +managed-aws-java-sdk-v1 = '1.12.320' managed-aws-java-sdk-v2 = '2.17.290' managed-aws-lambda = '1.2.1' managed-aws-lambda-events = '3.11.0' From 671d0181634becd44be2b3a3fed8bf09ebdb1daf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 17 Oct 2022 11:12:01 +0200 Subject: [PATCH 45/51] Update dependency software.amazon.awscdk:aws-cdk-lib to v2.46.0 (#1489) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 883607bc21..c4ba6e0dac 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,7 +9,7 @@ managed-aws-java-sdk-v2 = '2.17.290' managed-aws-lambda = '1.2.1' managed-aws-lambda-events = '3.11.0' managed-aws-serverless-core = '1.9' -managed-aws-cdk-lib='2.45.0' +managed-aws-cdk-lib='2.46.0' servlet-api = "2.5" slf4j = "1.7.36" From e2e48b5675f27c9a016ad89acc322e4b8f809d68 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 17 Oct 2022 11:12:10 +0200 Subject: [PATCH 46/51] Update plugin io.micronaut.build.shared.settings to v5.3.15 (#1488) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 5893d221f1..526c85a816 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -6,7 +6,7 @@ pluginManagement { } plugins { - id("io.micronaut.build.shared.settings") version "5.3.14" + id("io.micronaut.build.shared.settings") version "5.3.15" } rootProject.name = "aws-parent" From 1ce9a2f6f7bbe42caf26396373abb2cc27d4d0f0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 17 Oct 2022 11:12:20 +0200 Subject: [PATCH 47/51] Update mikepenz/action-junit-report action to v3.5.2 (#1487) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/graalvm.yml | 2 +- .github/workflows/gradle.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/graalvm.yml b/.github/workflows/graalvm.yml index 8f3f1c04aa..d0cf09395b 100644 --- a/.github/workflows/graalvm.yml +++ b/.github/workflows/graalvm.yml @@ -58,7 +58,7 @@ jobs: PREDICTIVE_TEST_SELECTION: "${{ github.event_name == 'pull_request' && 'true' || 'false' }}" - name: Publish Test Report if: always() - uses: mikepenz/action-junit-report@v3.5.1 + uses: mikepenz/action-junit-report@v3.5.2 with: check_name: GraalVM CE CI / Test Report (Java ${{ matrix.java }}) report_paths: '**/build/test-results/test/TEST-*.xml' diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 51d9160636..61dbb53d75 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -60,7 +60,7 @@ jobs: PREDICTIVE_TEST_SELECTION: "${{ github.event_name == 'pull_request' && 'true' || 'false' }}" - name: Publish Test Report if: always() - uses: mikepenz/action-junit-report@v3.5.1 + uses: mikepenz/action-junit-report@v3.5.2 with: check_name: Java CI / Test Report (${{ matrix.java }}) report_paths: '**/build/test-results/test/TEST-*.xml' From 51409e7595f18be4d315fb142711d40e114c13d8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 17 Oct 2022 11:12:29 +0200 Subject: [PATCH 48/51] Update aws-java-sdk monorepo to v1.12.321 (#1485) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c4ba6e0dac..93ceef5d7f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,7 @@ fileupload = '0.0.5' jetty = '9.4.49.v20220914' logback-json-classic = '0.1.5' managed-alexa-ask-sdk = "2.44.0" -managed-aws-java-sdk-v1 = '1.12.320' +managed-aws-java-sdk-v1 = '1.12.321' managed-aws-java-sdk-v2 = '2.17.290' managed-aws-lambda = '1.2.1' managed-aws-lambda-events = '3.11.0' From 9b512dfe0aaded418e47c6ce4ed7ea37a1a952a9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 17 Oct 2022 13:20:28 +0200 Subject: [PATCH 49/51] Update aws-java-sdk-v2 monorepo to v2.17.292 (#1486) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Sergio del Amo --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 93ceef5d7f..9db563b78b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ jetty = '9.4.49.v20220914' logback-json-classic = '0.1.5' managed-alexa-ask-sdk = "2.44.0" managed-aws-java-sdk-v1 = '1.12.321' -managed-aws-java-sdk-v2 = '2.17.290' +managed-aws-java-sdk-v2 = '2.17.292' managed-aws-lambda = '1.2.1' managed-aws-lambda-events = '3.11.0' managed-aws-serverless-core = '1.9' From ddb01437167c6589c13d1f62a1c08b119d791211 Mon Sep 17 00:00:00 2001 From: Slavko Date: Mon, 17 Oct 2022 13:21:58 +0200 Subject: [PATCH 50/51] doc: Improve secrets-manager documentation (#1490) --- ...istributedconfigurationsecretsmanager.adoc | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/main/docs/guide/distributedconfiguration/distributedconfigurationsecretsmanager.adoc b/src/main/docs/guide/distributedconfiguration/distributedconfigurationsecretsmanager.adoc index 7e5b02127d..0c0d23a313 100644 --- a/src/main/docs/guide/distributedconfiguration/distributedconfigurationsecretsmanager.adoc +++ b/src/main/docs/guide/distributedconfiguration/distributedconfigurationsecretsmanager.adoc @@ -17,4 +17,25 @@ If you add `micronaut.application.name: myapp` to `bootstrap.yml` and you start image::secretsmanager.png[AWS Secrets Manager] -include::{includedir}configurationProperties/io.micronaut.aws.secretsmanager.SecretsManagerConfigurationProperties.adoc[] \ No newline at end of file +include::{includedir}configurationProperties/io.micronaut.aws.secretsmanager.SecretsManagerConfigurationProperties.adoc[] + +To avoid secret keys naming collision in the Micronaut application context, which is caused by the strict naming convention specified by some AWS services, add a configuration section to `src/main/resources/bootstrap.yml`. +For example, if you would like to use multiple RDS instances, you can do it in the following way: +[source, yaml] +.src/main/resources/bootstrap.yml +---- +micronaut: + config-client: + enabled: true +aws: + secretsmanager: + secrets: + - secret-name: rds + prefix: datasources.default + - secret-name: rds_backup + prefix: datasources.backup +---- +Note that `secret-name` is a name suffix of the secret configured in AWS Secret Manager. For example, to add `rds_backup` configuration for a `production` environment, add the `rds_backup` configuration entry to the `src/main/resources/bootstrap.yml` as shown in the example above. Also, create RDS secret in AWS Secret Manager with name `/config/application_prod/rds_backup` or `/config/[APPLICATION_NAME]_prod/rds_backup`. +Note that `prefix` is a unique key prefix that is prepended to all keys that belong to a given secret. + +To learn more about the Micronaut environments, go to https://docs.micronaut.io/latest/guide/#environments[Environments] From b41e83ce96bdb31e3d14aa79b9050195cba210df Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Mon, 17 Oct 2022 13:22:13 +0200 Subject: [PATCH 51/51] feat: Alexa Locale Resolution (#1483) --- .../CompositeHandlerInputLocaleResolver.java | 58 ++++++++++++++ .../DefaultHandlerInputLocaleResolver.java | 51 ++++++++++++ .../HandlerInputAbstractLocaleResolver.java | 43 ++++++++++ .../HandlerInputFixedLocaleResolver.java | 47 +++++++++++ ...lerInputLocaleResolutionConfiguration.java | 25 ++++++ ...caleResolutionConfigurationProperties.java | 80 +++++++++++++++++++ .../locale/HandlerInputLocaleResolver.java | 29 +++++++ .../locale/DefaultLocaleResolverSpec.groovy | 35 ++++++++ ...nputFixedLocaleResolverDisabledSpec.groovy | 19 +++++ ...HandlerInputFixedLocaleResolverSpec.groovy | 40 ++++++++++ .../HandlerInputLocaleResolverSpec.groovy | 52 ++++++++++++ .../docs/guide/alexa/localeresoultion.adoc | 7 ++ src/main/docs/guide/toc.yml | 1 + 13 files changed, 487 insertions(+) create mode 100644 aws-alexa/src/main/java/io/micronaut/aws/alexa/locale/CompositeHandlerInputLocaleResolver.java create mode 100644 aws-alexa/src/main/java/io/micronaut/aws/alexa/locale/DefaultHandlerInputLocaleResolver.java create mode 100644 aws-alexa/src/main/java/io/micronaut/aws/alexa/locale/HandlerInputAbstractLocaleResolver.java create mode 100644 aws-alexa/src/main/java/io/micronaut/aws/alexa/locale/HandlerInputFixedLocaleResolver.java create mode 100644 aws-alexa/src/main/java/io/micronaut/aws/alexa/locale/HandlerInputLocaleResolutionConfiguration.java create mode 100644 aws-alexa/src/main/java/io/micronaut/aws/alexa/locale/HandlerInputLocaleResolutionConfigurationProperties.java create mode 100644 aws-alexa/src/main/java/io/micronaut/aws/alexa/locale/HandlerInputLocaleResolver.java create mode 100644 aws-alexa/src/test/groovy/io/micronaut/aws/alexa/locale/DefaultLocaleResolverSpec.groovy create mode 100644 aws-alexa/src/test/groovy/io/micronaut/aws/alexa/locale/HandlerInputFixedLocaleResolverDisabledSpec.groovy create mode 100644 aws-alexa/src/test/groovy/io/micronaut/aws/alexa/locale/HandlerInputFixedLocaleResolverSpec.groovy create mode 100644 aws-alexa/src/test/groovy/io/micronaut/aws/alexa/locale/HandlerInputLocaleResolverSpec.groovy create mode 100644 src/main/docs/guide/alexa/localeresoultion.adoc diff --git a/aws-alexa/src/main/java/io/micronaut/aws/alexa/locale/CompositeHandlerInputLocaleResolver.java b/aws-alexa/src/main/java/io/micronaut/aws/alexa/locale/CompositeHandlerInputLocaleResolver.java new file mode 100644 index 0000000000..1220b2fefa --- /dev/null +++ b/aws-alexa/src/main/java/io/micronaut/aws/alexa/locale/CompositeHandlerInputLocaleResolver.java @@ -0,0 +1,58 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.aws.alexa.locale; + +import com.amazon.ask.dispatcher.request.handler.HandlerInput; +import io.micronaut.context.annotation.Primary; +import io.micronaut.core.annotation.NonNull; +import jakarta.inject.Singleton; + +import java.util.Arrays; +import java.util.Locale; +import java.util.Optional; + + +/** + * {@link Primary} {@link HandlerInputLocaleResolver} which evaluates every {@link HandlerInputLocaleResolver} by order to resolve a {@link java.util.Locale}. + * @author Sergio del Amo + * @since 3.10.0 + */ +@Primary +@Singleton +public class CompositeHandlerInputLocaleResolver extends HandlerInputAbstractLocaleResolver { + + private final HandlerInputLocaleResolver[] localeResolvers; + + /** + * @param localeResolvers Locale Resolvers + * @param handlerInputLocaleResolutionConfiguration Locale Resolution configuration for HTTP Requests + */ + public CompositeHandlerInputLocaleResolver(HandlerInputLocaleResolver[] localeResolvers, + HandlerInputLocaleResolutionConfiguration handlerInputLocaleResolutionConfiguration) { + super(handlerInputLocaleResolutionConfiguration); + this.localeResolvers = localeResolvers; + } + + @Override + @NonNull + public Optional resolve(@NonNull HandlerInput request) { + return Arrays.stream(localeResolvers) + .map(resolver -> resolver.resolve(request)) + .filter(Optional::isPresent) + .findFirst() + .orElse(Optional.empty()); + } +} diff --git a/aws-alexa/src/main/java/io/micronaut/aws/alexa/locale/DefaultHandlerInputLocaleResolver.java b/aws-alexa/src/main/java/io/micronaut/aws/alexa/locale/DefaultHandlerInputLocaleResolver.java new file mode 100644 index 0000000000..20c68653fc --- /dev/null +++ b/aws-alexa/src/main/java/io/micronaut/aws/alexa/locale/DefaultHandlerInputLocaleResolver.java @@ -0,0 +1,51 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.aws.alexa.locale; + +import com.amazon.ask.dispatcher.request.handler.HandlerInput; +import io.micronaut.core.annotation.NonNull; +import io.micronaut.core.util.StringUtils; +import jakarta.inject.Singleton; + +import java.util.Locale; +import java.util.Optional; + +/** + * Resolves {@link Locale} from the {@link HandlerInput} request. + * @see Request Locale. + * @author Sergio del Amo + * @since 3.10.0 + */ +@Singleton +public class DefaultHandlerInputLocaleResolver extends HandlerInputAbstractLocaleResolver { + + /** + * @param localeResolutionConfiguration The locale resolution configuration + */ + protected DefaultHandlerInputLocaleResolver(HandlerInputLocaleResolutionConfiguration localeResolutionConfiguration) { + super(localeResolutionConfiguration); + } + + @Override + @NonNull + public Optional resolve(@NonNull HandlerInput input) { + String languageTag = input.getRequest().getLocale(); + if (StringUtils.isEmpty(languageTag)) { + return Optional.empty(); + } + return Optional.of(Locale.forLanguageTag(languageTag)); + } +} diff --git a/aws-alexa/src/main/java/io/micronaut/aws/alexa/locale/HandlerInputAbstractLocaleResolver.java b/aws-alexa/src/main/java/io/micronaut/aws/alexa/locale/HandlerInputAbstractLocaleResolver.java new file mode 100644 index 0000000000..862967c7ae --- /dev/null +++ b/aws-alexa/src/main/java/io/micronaut/aws/alexa/locale/HandlerInputAbstractLocaleResolver.java @@ -0,0 +1,43 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.aws.alexa.locale; + +import com.amazon.ask.dispatcher.request.handler.HandlerInput; +import io.micronaut.core.util.locale.AbstractLocaleResolver; + +/** + * Provides an abstract class which implements {@link io.micronaut.core.util.LocaleResolver} and handles default locale resolution. + * @author Sergio del Amo + * @since 3.10.0 + */ +public abstract class HandlerInputAbstractLocaleResolver extends AbstractLocaleResolver implements HandlerInputLocaleResolver { + public static final Integer ORDER = 50; + + protected final HandlerInputLocaleResolutionConfiguration localeResolutionConfiguration; + + /** + * @param localeResolutionConfiguration The locale resolution configuration + */ + protected HandlerInputAbstractLocaleResolver(HandlerInputLocaleResolutionConfiguration localeResolutionConfiguration) { + super(localeResolutionConfiguration.getDefaultLocale()); + this.localeResolutionConfiguration = localeResolutionConfiguration; + } + + @Override + public int getOrder() { + return ORDER; + } +} diff --git a/aws-alexa/src/main/java/io/micronaut/aws/alexa/locale/HandlerInputFixedLocaleResolver.java b/aws-alexa/src/main/java/io/micronaut/aws/alexa/locale/HandlerInputFixedLocaleResolver.java new file mode 100644 index 0000000000..8eacd0d08f --- /dev/null +++ b/aws-alexa/src/main/java/io/micronaut/aws/alexa/locale/HandlerInputFixedLocaleResolver.java @@ -0,0 +1,47 @@ +/* + * Copyright 2017-2020 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.aws.alexa.locale; + +import com.amazon.ask.dispatcher.request.handler.HandlerInput; +import io.micronaut.context.annotation.Requires; +import io.micronaut.core.order.Ordered; +import io.micronaut.core.util.locale.FixedLocaleResolver; +import jakarta.inject.Singleton; + +/** + * Generic implementation of {@link io.micronaut.core.util.LocaleResolver} for fixed locale resolution. + * + * @author Sergio del Amo + * @since 3.10.0 + */ +@Singleton +@Requires(property = HandlerInputLocaleResolutionConfigurationProperties.PREFIX + ".fixed") +public class HandlerInputFixedLocaleResolver extends FixedLocaleResolver implements HandlerInputLocaleResolver { + + public static final Integer ORDER = Ordered.HIGHEST_PRECEDENCE + 100; + + /** + * @param localeResolutionConfiguration The locale resolution configuration + */ + public HandlerInputFixedLocaleResolver(HandlerInputLocaleResolutionConfiguration localeResolutionConfiguration) { + super(localeResolutionConfiguration.getFixed().orElseThrow(() -> new IllegalArgumentException("The fixed locale must be set"))); + } + + @Override + public int getOrder() { + return ORDER; + } +} diff --git a/aws-alexa/src/main/java/io/micronaut/aws/alexa/locale/HandlerInputLocaleResolutionConfiguration.java b/aws-alexa/src/main/java/io/micronaut/aws/alexa/locale/HandlerInputLocaleResolutionConfiguration.java new file mode 100644 index 0000000000..31ec58f6ac --- /dev/null +++ b/aws-alexa/src/main/java/io/micronaut/aws/alexa/locale/HandlerInputLocaleResolutionConfiguration.java @@ -0,0 +1,25 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.aws.alexa.locale; + +import io.micronaut.core.util.locale.LocaleResolutionConfiguration; + +/** + * @author Sergio del Amo + * @since 3.10.0 + */ +public interface HandlerInputLocaleResolutionConfiguration extends LocaleResolutionConfiguration { +} diff --git a/aws-alexa/src/main/java/io/micronaut/aws/alexa/locale/HandlerInputLocaleResolutionConfigurationProperties.java b/aws-alexa/src/main/java/io/micronaut/aws/alexa/locale/HandlerInputLocaleResolutionConfigurationProperties.java new file mode 100644 index 0000000000..a6b23a5317 --- /dev/null +++ b/aws-alexa/src/main/java/io/micronaut/aws/alexa/locale/HandlerInputLocaleResolutionConfigurationProperties.java @@ -0,0 +1,80 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.aws.alexa.locale; + +import io.micronaut.aws.alexa.conf.AlexaSkillConfigurationProperties; +import io.micronaut.context.annotation.ConfigurationProperties; +import io.micronaut.core.annotation.NonNull; +import io.micronaut.core.annotation.Nullable; + +import java.util.Locale; +import java.util.Optional; + +/** + * {@link ConfigurationProperties} implementation of {@link HandlerInputLocaleResolutionConfiguration}. + * @author Sergio del Amo + * @since 3.10.0 + */ +@ConfigurationProperties(HandlerInputLocaleResolutionConfigurationProperties.PREFIX) +public class HandlerInputLocaleResolutionConfigurationProperties implements HandlerInputLocaleResolutionConfiguration { + public static final String PREFIX = AlexaSkillConfigurationProperties.PREFIX + ".locale-resolution"; + + /** + * The default locale. + */ + @SuppressWarnings("WeakerAccess") + public static final String DEFAULT_LOCALE = "en-US"; + + private Locale fixed; + private Locale defaultLocale = Locale.forLanguageTag(DEFAULT_LOCALE); + + /** + * @return The fixed locale + */ + @NonNull + public Optional getFixed() { + return Optional.ofNullable(fixed); + } + + /** + * Sets the fixed locale. Any of ar-SA, de-DE, en-AU, en-CA, en-GB, en-IN, en-US, es-ES, es-MX, es-US, fr-CA, fr-FR, hi-IN, it-IT, ja-JP, pt-BR + * + * @param fixed The fixed locale + */ + public void setFixed(@Nullable Locale fixed) { + this.fixed = fixed; + } + + /** + * @return The locale to be used if one cannot be resolved. + */ + @Override + @NonNull + public Locale getDefaultLocale() { + return defaultLocale; + } + + /** + * Sets the locale that will be used if the locale cannot be + * resolved through any means. Defaults to {@value #DEFAULT_LOCALE}. + * + * @param defaultLocale The default locale. + */ + public void setDefaultLocale(@NonNull Locale defaultLocale) { + this.defaultLocale = defaultLocale; + } + +} diff --git a/aws-alexa/src/main/java/io/micronaut/aws/alexa/locale/HandlerInputLocaleResolver.java b/aws-alexa/src/main/java/io/micronaut/aws/alexa/locale/HandlerInputLocaleResolver.java new file mode 100644 index 0000000000..11b8fd6561 --- /dev/null +++ b/aws-alexa/src/main/java/io/micronaut/aws/alexa/locale/HandlerInputLocaleResolver.java @@ -0,0 +1,29 @@ +/* + * Copyright 2017-2022 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.aws.alexa.locale; + +import com.amazon.ask.dispatcher.request.handler.HandlerInput; +import io.micronaut.core.annotation.Indexed; +import io.micronaut.core.util.LocaleResolver; + +/** + * Responsible for determining the current locale for a {@link HandlerInput} event. + * @author Sergio del Amo + * @since 3.10.0 + */ +@Indexed(HandlerInputLocaleResolver.class) +public interface HandlerInputLocaleResolver extends LocaleResolver { +} diff --git a/aws-alexa/src/test/groovy/io/micronaut/aws/alexa/locale/DefaultLocaleResolverSpec.groovy b/aws-alexa/src/test/groovy/io/micronaut/aws/alexa/locale/DefaultLocaleResolverSpec.groovy new file mode 100644 index 0000000000..31228af674 --- /dev/null +++ b/aws-alexa/src/test/groovy/io/micronaut/aws/alexa/locale/DefaultLocaleResolverSpec.groovy @@ -0,0 +1,35 @@ +package io.micronaut.aws.alexa.locale + +import com.amazon.ask.dispatcher.request.handler.HandlerInput +import com.amazon.ask.model.Request +import io.micronaut.context.BeanContext +import io.micronaut.context.annotation.Property +import io.micronaut.core.util.LocaleResolver +import io.micronaut.test.extensions.spock.annotation.MicronautTest +import jakarta.inject.Inject +import spock.lang.Specification + +@MicronautTest(startApplication = false) +class DefaultLocaleResolverSpec extends Specification { + + @Inject + BeanContext beanContext + + @Inject + LocaleResolver localeResolver + + void "default locale is en-US"() { + given: + def request = Stub(Request) { + getLocale() >> "" + } + def input = Stub(HandlerInput) { + getRequest() >> request + } + when: + Locale locale = localeResolver.resolveOrDefault(input) + + then: + new Locale("en", "US") == locale + } +} diff --git a/aws-alexa/src/test/groovy/io/micronaut/aws/alexa/locale/HandlerInputFixedLocaleResolverDisabledSpec.groovy b/aws-alexa/src/test/groovy/io/micronaut/aws/alexa/locale/HandlerInputFixedLocaleResolverDisabledSpec.groovy new file mode 100644 index 0000000000..1782c9beec --- /dev/null +++ b/aws-alexa/src/test/groovy/io/micronaut/aws/alexa/locale/HandlerInputFixedLocaleResolverDisabledSpec.groovy @@ -0,0 +1,19 @@ +package io.micronaut.aws.alexa.locale + +import io.micronaut.context.BeanContext +import io.micronaut.test.extensions.spock.annotation.MicronautTest +import jakarta.inject.Inject +import spock.lang.Specification + +@MicronautTest(startApplication = false) +public class HandlerInputFixedLocaleResolverDisabledSpec extends Specification { + + @Inject + BeanContext beanContext + + void "HandlerInputFixedLocaleResolver is disabled by default"() { + expect: + !beanContext.containsBean(HandlerInputFixedLocaleResolver) + } + +} diff --git a/aws-alexa/src/test/groovy/io/micronaut/aws/alexa/locale/HandlerInputFixedLocaleResolverSpec.groovy b/aws-alexa/src/test/groovy/io/micronaut/aws/alexa/locale/HandlerInputFixedLocaleResolverSpec.groovy new file mode 100644 index 0000000000..ff7aa95f46 --- /dev/null +++ b/aws-alexa/src/test/groovy/io/micronaut/aws/alexa/locale/HandlerInputFixedLocaleResolverSpec.groovy @@ -0,0 +1,40 @@ +package io.micronaut.aws.alexa.locale + +import com.amazon.ask.dispatcher.request.handler.HandlerInput +import com.amazon.ask.model.Request +import io.micronaut.context.BeanContext +import io.micronaut.context.annotation.Property +import io.micronaut.core.util.LocaleResolver +import io.micronaut.test.extensions.spock.annotation.MicronautTest +import jakarta.inject.Inject +import spock.lang.Specification + +@Property(name = "alexa.locale-resolution.fixed", value = "es-ES") +@MicronautTest(startApplication = false) +class HandlerInputFixedLocaleResolverSpec extends Specification { + + @Inject + BeanContext beanContext + + @Inject + LocaleResolver localeResolver + + void "fixed locale takes precedence"() { + given: + def request = Stub(Request) { + getLocale() >> "en-US" + } + def input = Stub(HandlerInput) { + getRequest() >> request + } + + expect: + beanContext.containsBean(HandlerInputFixedLocaleResolver) + + when: + Locale locale = localeResolver.resolveOrDefault(input) + + then: + new Locale("es", "ES") == locale + } +} diff --git a/aws-alexa/src/test/groovy/io/micronaut/aws/alexa/locale/HandlerInputLocaleResolverSpec.groovy b/aws-alexa/src/test/groovy/io/micronaut/aws/alexa/locale/HandlerInputLocaleResolverSpec.groovy new file mode 100644 index 0000000000..d35b0b948e --- /dev/null +++ b/aws-alexa/src/test/groovy/io/micronaut/aws/alexa/locale/HandlerInputLocaleResolverSpec.groovy @@ -0,0 +1,52 @@ +package io.micronaut.aws.alexa.locale + +import com.amazon.ask.dispatcher.request.handler.HandlerInput +import com.amazon.ask.model.Request +import io.micronaut.core.util.LocaleResolver +import io.micronaut.test.extensions.spock.annotation.MicronautTest +import jakarta.inject.Inject +import spock.lang.Specification +import spock.lang.Unroll + +@MicronautTest(startApplication = false) +class HandlerInputLocaleResolverSpec extends Specification { + + @Inject + LocaleResolver localeResolver + + @Unroll + void "resolve locale from handler input"(String languageCode, Locale expected) { + given: + def request = Stub(Request) { + getLocale() >> languageCode + } + def input = Stub(HandlerInput) { + getRequest() >> request + } + + when: + Locale locale = localeResolver.resolveOrDefault(input) + + then: + expected == locale + + where: + languageCode | expected + 'ar-SA' | new Locale("ar", "SA") + 'de-DE' | Locale.GERMANY + 'en-AU' | new Locale("en", "AU") + 'en-CA' | Locale.CANADA + 'en-GB' | Locale.UK + 'en-IN' | new Locale("en", "IN") + 'en-US' | Locale.US + 'es-ES' | new Locale("es", "ES") + 'es-MX' | new Locale("es", "MX") + 'es-US' | new Locale("es", "US") + 'fr-CA' | Locale.CANADA_FRENCH + 'fr-FR' | Locale.FRANCE + 'hi-IN' | new Locale("hi", "IN") + 'it-IT' | Locale.ITALY + 'ja-JP' | Locale.JAPAN + 'pt-BR' | new Locale("pt", "BR") + } +} diff --git a/src/main/docs/guide/alexa/localeresoultion.adoc b/src/main/docs/guide/alexa/localeresoultion.adoc new file mode 100644 index 0000000000..74af387d54 --- /dev/null +++ b/src/main/docs/guide/alexa/localeresoultion.adoc @@ -0,0 +1,7 @@ +To resolve the https://developer.amazon.com/en-US/docs/alexa/custom-skills/request-and-response-json-reference.html#request-locale[Request Locale] inject a bean of type `api:aws.alexa.locale.HandlerInputLocaleResolver[]`. + +You can force a fixed locale or a custom default locale via configuration: + +include::{includedir}configurationProperties/io.micronaut.aws.alexa.locale.HandlerInputLocaleResolutionConfigurationProperties.adoc[] + +If the built-in methods do not meet your use case, create a bean of type api:aws.alexa.locale.HandlerInputLocaleResolver[] and set its order (through the `getOrder` method) relative to the existing resolvers. diff --git a/src/main/docs/guide/toc.yml b/src/main/docs/guide/toc.yml index d4ea9ae8e3..e57e56ab7f 100644 --- a/src/main/docs/guide/toc.yml +++ b/src/main/docs/guide/toc.yml @@ -69,6 +69,7 @@ alexa: webservice: Alexa Skill as a Web Service ssmlbuilder: SSML Builder flashbriefings: Flash Briefings + localeresoultion: Locale Resolution distributedconfiguration: title: Distributed Configuration distributedconfigurationsecretsmanager: AWS Secrets Manager